C #: Kuo skiriasi saugaus sriegio ir atominė?


Atsakymas 1:

Saugaus sriegio priemonės nepainioja, kai prieinamos iš kelių gijų; atominė reiškia nedalomą, tame kontekste lygiavertę nepertraukiamam.

Norėdami įdiegti spynos, turite du pasirinkimus:

  1. Turi aparatinę atominių operacijų palaikymą - specialias sudėtines instrukcijas, kurios vykdomos kaip visuma, pavyzdžiui, „Test-and-set.Bet“ protingas (ir patiri pasekmes) - Petersono algoritmas.

Detaliame jūsų pavyzdyje abu yra nesaugūs; jei aš teisingai supratau, tu turi omenyje kažką panašaus:

visuomenės klasė Nesaugi
{
    privatus objektas ulock = naujas objektas ();

    public int Nesaugus1 {gauti; rinkinys; } = 0;

    privatus int _unsafe2 = 0;
    public int Nesaugus2
    {
        gauti
        {
            užraktas (ulock)
            {
                grįžti _unsafe2;
            }
        }

        rinkinys
        {
            užraktas (ulock)
            {
                _unsafe2 = reikšmė;
            }
        }
    }
}

Testo kodas:

var u = naujas nesaugus ();

Parallel.For (0, 10000000, _ => {u.Unsafe1 ++;});
Parallel.For (0, 10000000, _ => {u.Unsafe2 ++;});

Console.WriteLine (string.Format („{0} - {1}“, u.Unsafe1, u.Unsafe2));

Rezultatas (vienas iš daugelio galimų):

4648265 - 4149827

Abiem atvejais daugiau nei pusė atnaujinimų dingo.

Priežastis ta, kad ++ nėra atominis - tai iš tikrųjų trys atskiros operacijos:

  1. Gaukite vertę.Pridėkite 1 prie reikšmės.Nustatykite vertę.

Mes galime tai išspręsti atlikdami atomazgos didėjimo operaciją - tai padaryti yra daug būdų, tačiau yra du:

vieša klasė saugi
{
    privataus objekto užraktas = naujas objektas ();

    public int Safe1 {gauti; rinkinys; }
    viešas negaliojimas „SafeIncrement1“ ()
    {
        užraktas (ulock)
        {
            tai.Sauga1 ++;
        }
    }

    privatus int _safe2 = 0;
    public int Safe2
    {
        gauti
        {
            grįžti _safe2;
        }
        rinkinys
        {
            _safe2 = vertė;
        }
    }
    visuomenės negaliojanti „SafeIncrement2“ ()
    {
        Susipynę.įtraukimas (nuoroda _safe2);
    }
}

Testo kodas:

var s = naujas seifas ();

Parallel.For (0, 10000000, _ => {s.SafeIncrement1 ();});
Parallel.For (0, 10000000, _ => {s.SafeIncrement2 ();});

„Console.WriteLine“ (string.Format („{0} - {1}“, s.Safe1, s.Safe2));

Rezultatai yra teisingi abiem atvejais. Pirmasis tiesiog užrakina visą kompozicinę ++ operaciją, o antrasis naudoja aparatūros palaikymą atominėms operacijoms.

Atkreipkite dėmesį į antrą aukščiau pateiktą variantą su „Interlocked.Increment“ yra daug greitesnis, tačiau iš tikrųjų yra žemesnio lygio ir ribotas tuo, ką gali padaryti iš dėžutės; tačiau blokuoto paketo operacijas galima naudoti:

  1. Pažįstami spynos, vadinamos „pesimistiniu suderinamumu“, nes, jų manymu, operacija bus nutraukta, todėl nesivarginkite pradėti tol, kol neįsigytų kokio nors bendro ištekliaus. „Užrakinti kodą“, a.k. Palyginkite ir keiskite, naudodami specialią „kanarėlių“ reikšmę, kurią įrašote pradžioje, tada įsitikinkite, kad pabaigoje jūsų atžvilgiu nepasikeitė; Idėja yra ta, kad jei ateis kitas siūlas, jis užmuš kanarėlę, todėl jūs žinote, kad iš naujo bandykite atlikti savo operaciją iš pat pradžių. Tam reikia, kad ir jūsų kodas būtų atominis - negalite rašyti tarpinių rezultatų į bendrinamą būseną, turite arba visiškai pasisekti, arba visiškai nepavykti (tarsi neatlikote jokių operacijų).

Atsakymas 2:

Du visiškai skirtingi dalykai. Saugu gija - funkcija, parašyta taip, kad ją būtų galima pakartoti daugybe skirtingų gijų, kiekvienai gijai nesutrikdant kitos gijos operacijos (pavyzdžiui, keičiant reikšmę, jei kintamasis, kurį naudoja kitas gija)

Atominė priemonė (jei kuriu, kur važiuoju) sukuria vieną objekto egzempliorių - kad ir kaip dažnai jis būtų nurodytas, jūs visada matote, kad vienas egzempliorius (iš bet kurios gijos)


Atsakymas 3:

Atominės operacijos yra būdas pasiekti sriegio saugumą naudojant tam tikrus užraktus, tokius kaip „Mutexes“ ar „Semaphores“, kurie naudoja atomines operacijas iš vidaus, arba įgyvendindami nemokamą sinchronizaciją be užraktų ir atminties tvorų.

Taigi atominės operacijos, susijusios su primityvių duomenų tipais, yra priemonė gijų saugumui pasiekti, tačiau automatiškai neužtikrina gijų saugos, nes paprastai atliekate kelias operacijas, kurios priklauso viena nuo kitos. Turite užtikrinti, kad šios operacijos būtų atliekamos be trikdžių, pvz., Naudojant „Mutexes“.

Taip, rašyti vieną iš šių atominių duomenų tipų „c #“ yra saugu, tačiau tai neužtikrina funkcijos, kurią naudojate jų gijoje. Tai tik užtikrina, kad vienas rašymas bus teisingai vykdomas, net jei antrasis gija prie jo prieis „tuo pačiu metu“. Nepaisant to, neužtikrinama, kad sekantis skaitymas iš dabartinio gijos gautų anksčiau parašytą vertę, nes jai galėjo būti parašyta kita gija, tik kad nuskaityta vertė yra teisinga.