Tarkime, yra klasė, kurioje yra viešas int skaitiklio laukas, kurį galima pasiekti keliomis gijomis, ir šis skaičius tik didės arba sumažės.
Įtraukiant šį lauką, kuri iš šių schemų turėtų būti naudojama ir kodėl?
- lock(this.locker) this.counter++;
- Interlocked.Increment(nuoroda this.counter);
- Pakeiskite skaitiklio prieigos modifikatorių į viešą nepastovų
Blogiausia (nė vienas iš jų iš tikrųjų neveikia)
Pakeiskite skaitiklio prieigos modifikatorių į viešą nepastovų
Šis metodas iš tikrųjų nėra saugus, o esmė apie nepastovumą yra ta, kad kelios gijos, veikiančios keliuose procesoriuose, buferinius duomenis ir pertvarko vykdomas instrukcijas.
Jei jis nepastovus, kai procesorius A padidėja verte, procesorius B turi šiek tiek palaukti, kol pamatys padidėjusią vertę, o tai gali sukelti problemų.
Jei jis nepastovus, tai užtikrina, kad abu procesoriai vienu metu matys tą pačią vertę. Tačiau tai neišvengia kryžminių skaitymo ir rašymo operacijų.
Kintamojo vertės pridėjimas iš tikrųjų trunka tris veiksmus
1. Skaitymas, 2. Pridėti 3. Rašyti
Tarkime, kad gija A skaitiklio reikšmę nuskaito kaip 1 ir nėra pasirengusi didinti, tada gija B taip pat nuskaito skaitiklio reikšmę kaip 1, o tada abi gijos pradeda atlikti inkrementines ir rašymo operacijas. Galutinio skaitiklio vertė yra 2. Tai neteisinga, abi gijos atliko padidinimo operaciją, o teisingas rezultatas turėtų būti 3. Taigi ženklinti jį kaip lakų yra tiesiog nesaugu.
Tai geriau
lock(this.locker) this.counter++;
Tokiu būdu tai saugu (žinoma, nepamirškite užrakinti visur, kur norite pasiekti this.counter). Tai neleidžia jokiai kitai gijai vykdyti užrakinto kodo. Tai taip pat apsaugo nuo aukščiau minėtos kelių procesorių instrukcijų sekos problemos. Problema ta, kad užraktas veikia lėtai, o jei naudojate užraktą kitose nesusijusiose vietose, jis gali užblokuoti kitas gijas.
Geriausias
Interlocked.Increment(nuoroda this.counter);
Tai saugu ir labai efektyvu. Jis atlieka skaitymą, didinimą, rašymą tris operacijas viename atome, nepertraukiamas viduryje. Kadangi tai neturi įtakos kitam kodui, jums nereikia prisiminti spynų kitur. Ir tai taip pat labai greita (kaip MSDN sako, šiandienos procesoriai, tai dažnai tik instrukcija).
Bet nesu visiškai tikras, ar jis taip pat gali išspręsti procesoriaus instrukcijų užsakymo problemą, ar jį reikia naudoti kartu su nepastoviu ir šiuo prieaugiu.
Papildymas: Kokias problemas gerai išsprendžia lakus?
Kadangi nepastovus negali užkirsti kelio kelių gijų, ką jis gali padaryti? Geras pavyzdys yra tai, kad turite dvi gijas, viena visada rašo į kintamąjį, tarkime, šis kintamasis yra queneLength, o kitas visada skaito duomenis iš šio kintamojo. Jei queueLenght nėra nepastovus, gija A gali skaityti 5 kartus, bet gija B gali matyti uždelstus duomenis arba net duomenis neteisinga tvarka. Vienas iš sprendimų yra naudoti užraktą, tačiau šiuo atveju taip pat galite naudoti lakiąją. Tai užtikrina, kad gija B visada matys naujausius duomenis, parašytus gijos A, tačiau ši logika veikia tik tuo atveju, jei jų neskaitote rašydami ir jei nerašote skaitydami. Kai kelios gijos nori atlikti skaitymo, modifikavimo ir rašymo operacijas, turite naudoti užrakintą arba užrakinti.
|