Ką žinote apie skaitytojo ir rašytojo problemą?
sobes.tech AI
Atsakymas iš AI
Klausimas ir rašytojo problema yra klasikinė sinchronizacijos užduotis bendriems duomenims daugiagijose aplinkose.
Problemos esmė:
- Daugybė srautų (skaitančiųjų) vienu metu skaito duomenis.
- Daugybė srautų (rašytojų) vienu metu keičia duomenis.
Reikalavimai:
- Skaitytojai gali skaityti vienu metu be apribojimų.
- Rašytojai turi išskirtinę prieigą prie duomenų (kol rašytojas dirba, niekas kitas – nei skaitytojai, nei rašytojai – negali prieiti).
- Vienu metu gali dirbti tik vienas rašytojas.
- Jei rašytojas laukia prieigos, nauji skaitytojai neturi jos gauti iki rašytojas baigs savo darbą. Šis taisyklė išvengia "rašytojų bado".
Sprendimai iOS:
-
NSLock: Paprasčiausias mechanizmas, bet ne optimalus šiai užduočiai, nes blokuoja tiek skaitymą, tiek rašymą.
-
NSRecursiveLock: Leidžia vienam ir tam pačiam srautui gauti blokavimą kelis kartus. Nenaudojamas.
-
NSCondition: Lankstesnis mechanizmas, leidžiantis srautams laukti tam tikrų sąlygų. Galima naudoti skaitytojų/rašytojų logikai įgyvendinti, bet reikalauja rankinio blokavimo ir sąlygų valdymo.
-
Serial Dispatch Queue (GCD): Sukurkite vieną eilę visoms skaitymo ir rašymo operacijoms. Rašymai vykdomi sinchroniškai, skaitymas gali būti atliekamas asinchroniškai, bet tik po ankstesnių operacijų pabaigos. Tai paprastas sprendimas, bet ne optimalus skaitymui, nes skaitymas negali būti vykdomas lygiagrečiai.
let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent) func readData() { readWriteQueue.async { // Duomenų skaitymo logika print("Skaitoma duomenys...") } } func writeData() { readWriteQueue.sync(flags: .barrier) { // Duomenų rašymo logika print("Rašoma duomenys...") } } -
Konkuruojanti Dispatch Queue su Barrier (GCD): Optimalus sprendimas daugeliu atvejų. Naudojama konkurencinga eilė paraleliniam skaitymui. Rašymui naudojamas
.barrieržymeklis. Kai.barrieroperacija yra eilėje, ji laukia visų ankstesnių operacijų pabaigos, tada vykdoma pati, ir kol ji vyksta, jokios kitos operacijos (nei skaitymas, nei rašymas) nevykdomos. Po barjerinės operacijos eilė vėl tampa konkurencinga. Taip, skaitymas vyksta lygiagrečiai, o rašymas yra išskirtinis.let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent) func readData() { readWriteQueue.async { // Duomenų skaitymo logika print("Skaitoma duomenys...") } } func writeData() { readWriteQueue.sync(flags: .barrier) { // Duomenų rašymo logika print("Rašoma duomenys...") } }Tai tas pats kodas kaip ir serial eilės atveju, bet atributas
.concurrentpadaro eilę konkurencingą..barrieržymeklis verčia eilę elgtis kaip sekančią tik šiai operacijai. -
os_unfair_lock (iOS 10+): Žemo lygio užraktas, efektyvesnis nei
NSLock. Naudojamas sudėtingesnės skaitytojų-rašytojų logikos įgyvendinimui rankiniu būdu, jei reikalingas maksimalus našumas arba specifinis elgesys. Reikalauja atsargumo naudojant.
Sprendimo pasirinkimas priklauso nuo konkrečių programos reikalavimų, skaitymo ir rašymo operacijų dažnumo ir reikalingo našumo lygio. Daugeliu atvejų, naudoti konkurencingą dispatch eilę su .barrier žymekliu yra subalansuotas ir rekomenduojamas požiūris iOS.