Sobes.tech
Middle+

Mida te tead probleemist lugeja ja kirjaniku?

sobes.tech AI

Vastus AI-lt

Lugeja ja kirjanikprobleem on klassikaline sünkroniseerimise ülesanne ühiste andmete juurdepääsu tagamiseks mitme niidiga keskkonnas.

Probleemi olemus:

  • Mitmed niidid (lugejad) loevad andmeid samaaegselt.
  • Mitmed niidid (kirjanikud) muudavad andmeid samaaegselt.

Nõuded:

  • Lugejad võivad lugeda samaaegselt ilma piiranguteta.
  • Kirjanikel on eksklusiivne juurdepääs andmetele (kuni kirjanik töötab, ei saa keegi teine – ei lugejad ega kirjanikud – sellele ligi).
  • Samal ajal võib töötada ainult üks kirjanik.
  • Kui kirjanik ootab juurdepääsu, ei tohi uued lugejad seda saada enne, kui kirjanik lõpetab oma töö. See reegel väldib "kirjanike nälga".

Lahendused iOS:

  • NSLock: Lihtsaim mehhanism, kuid mitte optimaalne selle ülesande jaoks, kuna blokeerib nii lugemise kui kirjutamise.

  • NSRecursiveLock: Annab võimaluse samal niidil saada lukustus mitu korda. Ei kasutata.

  • NSCondition: Paindlikum mehhanism, mis võimaldab niitidel oodata teatud tingimusi. Seda saab kasutada lugejate/kirjanike loogika rakendamiseks, kuid nõuab käsitsi lukustamise ja tingimuste haldamist.

  • Serial Dispatch Queue (GCD): Looge üks järjekord kõigile lugemis- ja kirjutamisoperatsioonidele. Kirjutused toimuvad sünkroonselt, lugemine võib toimuda asünkroonselt, kuid ainult enne eelnevalt tehtud operatsioonide lõpetamist. See on lihtne lahendus, kuid mitte optimaalne lugemiseks, kuna lugemine ei saa toimuda paralleelselt.

    let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent)
    
    func readData() {
        readWriteQueue.async {
            // Andmete lugemise loogika
            print("Lugedes andmeid...")
        }
    }
    
    func writeData() {
        readWriteQueue.sync(flags: .barrier) {
            // Andmete kirjutamise loogika
            print("Kirjutades andmeid...")
        }
    }
    
  • Konkurentsivõimeline Dispatch Queue Barrieriga (GCD): Enamikul juhtudel optimaalne lahendus. Kasutatakse konkurentsivõimelist järjekorda paralleelse lugemise jaoks. Kirjutamiseks kasutatakse .barrier märki. Kui .barrier operatsioon on järjekorras, ootab see kõigi eelnevate operatsioonide lõpetamist, seejärel täidetakse see ise, ja kuni see täidetakse, ei tehta muid operatsioone (ei lugemist ega kirjutamist). Pärast barjäärioperatsiooni muutub järjekord taas konkurentsivõimeliseks. Nii toimub lugemine paralleelselt, kuid kirjutamine on eksklusiivne.

    let readWriteQueue = DispatchQueue(label: "com.example.readwrite", attributes: .concurrent)
    
    func readData() {
        readWriteQueue.async {
            // Andmete lugemise loogika
            print("Lugedes andmeid...")
        }
    }
    
    func writeData() {
        readWriteQueue.sync(flags: .barrier) {
            // Andmete kirjutamise loogika
            print("Kirjutades andmeid...")
        }
    }
    

    See sama kood nagu sünkroonse järjekorra puhul, kuid atribuut .concurrent muudab järjekorra konkurentsivõimeliseks. .barrier märki sunnib järjekorda käituma nagu järjestikune ainult selle konkreetse operatsiooni jaoks.

  • os_unfair_lock (iOS 10+): Madala taseme lukustus, mis on tõhusam kui NSLock. Seda saab kasutada keerulisema lugemise/kirjutamise loogika käsitsi rakendamiseks, kui nõutakse maksimaalset jõudlust või spetsiifilist käitumist. Vajab ettevaatust kasutamisel.

Valik sõltub rakenduse konkreetsetest nõuetest, lugemis- ja kirjutamisoperatsioonide sagedusest ning vajaliku jõudluse tasemest. Enamasti on kõige tasakaalustatum ja soovitatav lähenemine iOS-is kasutada konkurentsivõimelist dispatch järjekorda .barrier märgisega.