C #: Kāda ir atšķirība starp drošu diegu un atomu?


Atbilde 1:

Vītnei drošs līdzeklis neizjaucas, ja tam piekļūst no vairākiem pavedieniem; atoms nozīmē nedalāmu, šajā kontekstā līdzvērtīgu nepārtrauktam.

Lai ieviestu slēdzenes, jums ir divas iespējas:

  1. Nodrošiniet aparatūras atbalstu atomu operācijām - īpašas saliktas instrukcijas, kuras tiek izpildītas kopumā, piemēram, Test-and-set.Be smart (un cieš no sekām) - Pētersona algoritms.

Detalizētā jūsu piemērā abi ir nedroši; ja es pareizi sapratu, jūs domājat kaut ko līdzīgu:

sabiedriskā klase Nedroši
{
    privāts objekts ulock = jauns objekts ();

    public int Nedroši1 {get; komplekts; } = 0;

    privāts int _unsafe2 = 0;
    public int Nedroši2
    {
        gūt
        {
            slēdzene (ulock)
            {
                atgriezt _unsafe2;
            }
        }

        komplekts
        {
            slēdzene (ulock)
            {
                _unsafe2 = vērtība;
            }
        }
    }
}

Pārbaudes kods:

var u = jauns Nedrošs ();

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

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

Rezultāts (viens no daudziem iespējamiem):

4648265 - 4149827

Abos gadījumos vairāk nekā puse atjauninājumu pazuda.

Iemesls ir tāds, ka ++ nav atoms - tās faktiski ir trīs atsevišķas operācijas:

  1. Iegūstiet vērtību.Pievienojiet 1. vērtībai.Statiet vērtību.

Mēs to varam labot, nodrošinot atomu pieauguma operāciju - to var izdarīt daudzos veidos, taču šeit ir divi veidi:

sabiedriskā klase Droša
{
    privāts objekts slock = jauns objekts ();

    public int Safe1 {get; komplekts; }
    publisks spēkā neesošs SafeIncrement1 ()
    {
        slēdzene (ulock)
        {
            šo.Safe1 ++;
        }
    }

    privāts int _safe2 = 0;
    public int Safe2
    {
        gūt
        {
            atgriezt _safe2;
        }
        komplekts
        {
            _safe2 = vērtība;
        }
    }
    publisks spēkā neesošs SafeIncrement2 ()
    {
        Savstarpēji bloķēts.Inkrements (ref _safe2);
    }
}

Pārbaudes kods:

var s = jauns seifs ();

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

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

Rezultāti ir pareizi abos gadījumos. Pirmais tikai ievieto bloķēšanu ap visu salikto ++ darbību, bet otrais izmanto aparatūras atbalstu atomu operācijām.

Ņemiet vērā, ka otrais iepriekš minētais variants ar Interlocked.Increment ir daudz ātrāks, taču faktiski ir zemāks līmenis un ierobežots tajā, ko tas var darīt ārpus korpusa; tomēr bloķētajā paketē esošās operācijas var izmantot, lai īstenotu:

  1. Pazīstamās slēdzenes - ko sauc par “pesimistisku vienlaicīgumu”, jo tās pieņem, ka darbība tiks pārtraukta, tāpēc neraizējieties sākt, kamēr nav ieguvis kādu kopīgu resursu. “Bloķēšanas kods”, t.sk. “optimistiska vienlaicība” - izmantojot, piem. Salīdziniet un mainiet, un jūs izmantojat īpašu “kanārijputnu” vērtību, kuru reģistrējat sākumā, pēc tam pārliecinieties, ka beigās nekas nav mainījies zem jums. ideja ir tāda, ka, ja jums pievienosies cits pavediens, tas nogalinās kanārijputniņu, tāpēc jūs zināt, ka mēģiniet vēlreiz veikt darījumu no paša sākuma. Tas prasa, lai arī jūsu kods būtu atomu - starpposma rezultātus nevar rakstīt koplietotajā stāvoklī, vai nu pilnībā, vai pilnīgi neveiksmīgi (it kā jūs nebūtu veicis nekādas darbības).

Atbilde 2:

Divas pilnīgi atšķirīgas lietas. Drošs pavediens ir funkcija, kas uzrakstīta tā, ka to daudzkārt var izsaukt dažādi pavedieni, neveicot katra pavediena traucējumus cita pavediena darbībai (piemēram, mainot vērtību, ja mainīgais, kuru izmanto cits pavediens)

Atoms nozīmē (ja nonākšu tur, kur jūs dodaties) objekta viena eksemplāra izveidi, tāpēc neatkarīgi no tā, cik bieži uz to atsaucas, jūs vienmēr redzat, ka viens eksemplārs (no jebkura pavediena)


Atbilde 3:

Atomu operācijas ir veids, kā sasniegt pavedienu drošību, izmantojot vai nu atsevišķas slēdzenes, piemēram, Mutexes vai Semaphores, kuras iekšēji izmanto atomu operācijas, vai arī īstenojot bez atslēgas sinhronizāciju, izmantojot atomus un atmiņas žogus.

Tātad primitīvo datu tipu atomu operācijas ir līdzeklis pavedienu drošības sasniegšanai, bet automātiski nenodrošina pavedienu drošību, jo parasti jums ir vairākas operācijas, kas balstās viena uz otru. Jums jāpārliecinās, ka šīs darbības tiek veiktas bez pārtraukuma, piemēram, izmantojot Mutexes.

Jā, rakstīt vienu no šiem atomu datu tipiem c # ir droša pavedieniem, taču tas nenozīmē, ka funkcija, kuru jūs tos izmantojat pavedienā, nav droša. Tas tikai nodrošina, ka vienotā rakstīšana tiek pareizi izpildīta, pat ja otrs pavediens tam piekļūst "vienlaikus". Tomēr nākamais lasījums no pašreizējā pavediena netiek nodrošināts, lai iegūtu vērtību, kas iepriekš uzrakstīta, jo tam varētu būt rakstīts cits pavediens, tikai tas, ka nolasītā vērtība ir derīga.