Pieņemsim, ka ir klase, kas satur publisku int skaitītāja lauku, kuram var piekļūt vairāki pavedieni, un šis skaitlis tikai palielināsies vai samazināsies.
Pievienojot šo lauku, kuras no šīm shēmām ir jāizmanto un kāpēc?
- lock(this.locker) this.counter++;
- Interlocked.Increment(ref this.counter);
- Skaitītāja piekļuves modifikatora maiņa uz publisku svārstīgu
Sliktākais (neviens no tiem faktiski nedarbojas)
Skaitītāja piekļuves modifikatora maiņa uz publisku svārstīgu
Šī pieeja faktiski nav droša, un jautājums par gaistošo ir tas, ka vairāki pavedieni, kas darbojas uz vairākiem CPU, bufera datus un pārkārto izpildītās instrukcijas.
Ja tas nav gaistošs, kad CPU A palielinās par vērtību, CPU B ir jāgaida, lai redzētu palielināto vērtību, kas var radīt problēmas.
Ja tas ir svārstīgs, tas nodrošina, ka abi procesori vienlaicīgi redz vienu un to pašu vērtību. Bet tas neizvairās no transversālām lasīšanas un rakstīšanas operācijām.
Vērtības pievienošana mainīgajam faktiski aizņem trīs soļus
1. Lasīšana, 2. Pievienot 3. Rakstīt
Pieņemsim, ka pavediens A nolasa skaitītāja vērtību kā 1 un nav gatavs palielināt, tad pavediens B arī nolasa skaitītāja vērtību kā 1, un tad abi pavedieni sāk veikt pakāpeniskas un rakstīšanas operācijas. Galīgā skaitītāja vērtība ir 2. Tas nav pareizi, abi pavedieni ir veikuši palielināšanas operāciju, un pareizajam rezultātam jābūt 3. Tātad marķēt to kā gaistošo ir vienkārši nedroši.
Tas ir labāk
lock(this.locker) this.counter++;
Tādā veidā tas ir droši (protams, neaizmirstiet bloķēt visur, kur vēlaties piekļūt šim.skaitītājs). Tas neļauj jebkuram citam pavedienam izpildīt bloķēto kodu. Un tas arī novērš iepriekš minēto vairāku CPU instrukciju secības problēmu. Problēma ir tā, ka bloķēšana ir lēna, un, ja izmantojat bloķēšanu citās nesaistītās vietās, tā var bloķēt citus pavedienus.
Labākais
Interlocked.Increment(ref this.counter);
Tas ir droši un ļoti efektīvi. Tas veic lasīšanu, palielināšanu, rakstīšanu trīs operācijas vienā atomā, nepārtraucot vidū. Tā kā tas neietekmē citu kodu, jums nav jāatceras slēdzenes citur. Un tas ir arī ļoti ātrs (kā saka MSDN, mūsdienu procesoros bieži vien tā ir tikai instrukcija).
Bet es neesmu pilnīgi pārliecināts, vai tas var atrisināt arī CPU instrukciju pasūtīšanas problēmu, vai arī tas ir jāizmanto kopā ar gaistošo un šo pieaugumu.
Papildinājums: Kādas problēmas labi atrisina gaistošs?
Tā kā gaistošs nevar novērst daudzpavedienu, ko tas var darīt? Labs piemērs ir tas, ka jums ir divi pavedieni, viens vienmēr raksta mainīgajā, pieņemsim, ka šis mainīgais ir queneLength, bet otrs vienmēr nolasa datus no šī mainīgā. Ja queueLenght nav svārstīgs, pavediens A var nolasīt 5 reizes, bet pavediens B var redzēt aizkavētus datus vai pat datus nepareizā secībā. Viens risinājums ir izmantot slēdzeni, bet šajā gadījumā varat izmantot arī gaistošus. Tas nodrošina, ka pavediens B vienmēr redz jaunākos datus, ko raksta pavediens A, bet šī loģika darbojas tikai tad, ja jūs to nelasāt, kad to rakstāt, un ja jūs to nerakstāt, kad to lasāt. Kad vairāki pavedieni vēlas veikt lasīšanas, modificēšanas un rakstīšanas operācijas, jums jāizmanto bloķēts vai bloķēts.
|