Oletetaan, että on olemassa luokka, joka sisältää julkisen älykkyyslaskurin, johon pääsee käsiksi useilla säikeillä, ja tämä luku vain kasvaa tai pienenee.
Kun tätä kenttää lisätään, mitä seuraavista kaavoista tulisi käyttää, ja miksi?
- lukitse(this.locker) this.counter++;
- Interlocked.Increment (viitte tämä.laskuri);
- Muuta laskurin pääsyn modifikaattori julkiseksi volatiiliksi
Pahin (mikään niistä ei oikeasti toimi)
Muuta laskurin pääsyn modifikaattori julkiseksi volatiiliksi
Tämä lähestymistapa ei itse asiassa ole lainkaan turvallinen, ja epävakaan ongelman pointti on, että useat säikeet, jotka toimivat useilla suorittimilla, puskuroivat dataa ja järjestävät suoritetut käskyt uudelleen.
Jos se ei ole volatiili, CPU A nousee arvolla, CPU B:n täytyy odottaa hetki nähdäkseen arvon, mikä voi aiheuttaa ongelmia.
Jos se on epävakaa, se varmistaa, että molemmat suorittimet näkevät saman arvon samaan aikaan. Mutta se ei estä poikkileikkaavia luku- ja kirjoitustoimintoja.
Muuttujan arvon lisääminen vaatii itse asiassa kolme vaihetta
1. Lukeminen, 2. Lisää 3. Kirjoita
Oletetaan, että säie A lukee laskurin arvon arvoksi 1 eikä ole valmis kasvamaan, silloin säie B lukee laskurin arvon arvoksi 1, ja molemmat säikeet alkavat suorittaa inkrementaalisia ja kirjoitusoperaatioita. Viimeisen laskurin arvo on 2. Tämä ei pidä paikkaansa, molemmat säikeet ovat tehneet suurennusoperaation, ja oikea tulos pitäisi olla 3. Joten sen leimaaminen epävakaaksi on yksinkertaisesti vaarallista.
Se on parempi
lukitse(this.locker) this.counter++;
Näin se on turvallista (muista lukita kaikki, joihin haluat päästä tähän .counteriin, tietenkin). Se estää muita säikeitä suorittamasta lukittua koodia. Se myös estää edellä mainitun moniprosessorisen käskyjärjestysongelman. Ongelma on, että lukitus on hidas suorituskyvyltään, ja jos käytät lukkoa muissa eri paikoissa, se voi estää muut säikeet.
Paras
Interlocked.Increment (viitte tämä.laskuri);
Tämä on turvallista ja erittäin tehokasta. Se suorittaa luku-, kasvatus- ja kirjoitustoimintoja yhdessä atomissa keskeyttämättä keskellä. Koska se ei vaikuta muuhun koodiin, sinun ei tarvitse muistaa lukkoja muualla. Ja se on myös hyvin nopea (kuten MSDN sanoo, nykyprosessoreissa se on usein vain käsky).
Mutta en ole täysin varma, voiko se ratkaista myös CPU:n käskyjärjestysongelman, vai pitääkö sitä käyttää yhdessä volatilen ja tämän lisäyksen kanssa.
Lisäravinne: Mitä ongelmia Volatile ratkaisee hyvin?
Koska Volatile ei voi estää monisäikeistämistä, mitä se voi tehdä? Hyvä esimerkki on, että sinulla on kaksi säikettä, joista toinen kirjoittaa aina muuttujalle, sanotaan että tämä muuttuja on queneLength, ja toinen aina lukee dataa tästä muuttujasta. Jos queueLenght ei ole volatiili, säie A voi lukea 5 kertaa, mutta säie B saattaa nähdä viivästyneitä tietoja tai jopa väärässä järjestyksessä. Yksi ratkaisu on käyttää lukkoa, mutta tässä tapauksessa voit käyttää myös volatilea. Tämä varmistaa, että säie B näkee aina viimeisimmän datan, jonka säie A on kirjoittanut, mutta tämä logiikka toimii vain, jos et lue sitä kirjoittaessasi, ja jos et kirjoita sitä lukiessasi. Kun useat säikeet haluavat tehdä luku-muokkaa-kirjoitusoperaatioita, sinun täytyy käyttää Interlocked- tai Lock-toimintoja.
|