Cili është ndryshimi midis fillit të sigurt dhe atomik?


përgjigje 1:

Siguria e fillit do të thotë që nuk ka probleme kur qasni në fijet e shumta. Atomi do të thotë i pandashëm, në këtë kontekst sinonim i pandërprerë.

Ekzistojnë dy mënyra për të zbatuar bravë:

  1. Mbështetje e pajisjeve kompjuterike për operacionet atomike - udhëzime të veçanta për komplekset që ekzekutohen në tërësi, si psh B. Test- and-set.Be zgjuar (dhe mbani pasojat) - algoritmi i Peterson.

Në shembullin tuaj në detaje, të dy janë të pasigurt. nëse e kuptoj saktë, do të thuash diçka të tillë:

klasë publike Të pasigurt object objekt privat ulock = objekt i ri (); publik int Unsafe1 {merrni; caktuar; = 0; int private _unsafe2 = 0; publik int Unsafe2 {marr bllokoj (ulock) {kthimi _unsafe2; }} vendosur {bllokoj (ulock) {_unsafe2 = vlera; }}}}

Kodi i provës:

var u = i ri i pasigurt (); Paralelisht.Për (0, 10000000, _ => {u.Unsafe1 ++;}); Paralelisht.Për (0, 10000000, _ => {u.Unsafe2 ++;}); Console.WriteLine (varg.Format ("{0} - {1}", u.Unsafe1, u.Unsafe2));

Rezultati (një nga shumë të mundshëm):

4648265 - 4149827

Në të dy rastet, më shumë se gjysma e azhurnimeve u zhdukën.

Arsyeja për këtë është se ++ nuk është atomike - në të vërtetë ekzistojnë tre operacione të ndara:

  1. Merr vlerën. Shto 1 në vlerë.Vlera e vendosur.

Ne mund ta rregullojmë këtë duke siguruar një operacion shtesë që është atomik. Ka shumë mënyra për ta bërë këtë, por këtu janë dy:

klasë publike Sigurt sl objekt i objektit privat = objekt i ri (); int publik i sigurt1 {merrni; caktuar; } pavlefshme publike SafeIncrement1 () {bllokoj (ulock) {këtë.Safe1 ++; } int int intelektual privat _safe2 = 0; publik int Safe2 {merrni {kthim _safe2; } vendos {_safe2 = vlera; v} publiku i pavlefshëm SafeIncrement2 () {I bllokuar.Increment (ref _safe2); }}

Kodi i provës:

var s = reja e sigurt (); Paralelisht.Për (0, 10000000, _ => {jam SafeIncrement1 ();}); Paralelisht.Për (0, 10000000, _ => {jam SafeIncrement2 ();}); Console.WriteLine (varg.Format ("{0} - {1}", në Safe1, në Safe2));

Rezultatet janë të sakta në të dyja rastet. E para thjesht bllokon të gjithë operacionin Composite ++, ndërsa e dyta përdor mbështetje hardware për operacionet atomike.

Vini re se varianti i dytë më sipër me Interlocked.Increment është shumë më i shpejtë, por në të vërtetë ka një nivel më të ulët dhe ofron vetëm funksionalitet të kufizuar. Sidoqoftë, operacionet në paketën e mbyllur mund të përdoren për të zbatuar sa vijon:

  1. Bllokimet e njohura, të njohura si "paralelizëm pesimist" sepse supozojnë se procesi do të ndërpritet, nuk fillojnë derisa të kenë fituar një burim të përbashkët. Kur krahasoni dhe shkëmbeni, përdorni një vlerë të veçantë "Kanarie" që regjistroni në fillim, dhe pastaj sigurohuni që asgjë nuk ka ndryshuar nën ju në fund. Ideja është që një fije tjetër të vret kanarin, kështu që ju e dini që duhet të përsërisni transaksionin tuaj që në fillim. Kjo supozon se kodi juaj është atomik. Ju nuk mund të shkruani rezultate të përkohshme në statusin e përbashkët. Ju ose duhet të jeni plotësisht të suksesshëm ose të dështoni plotësisht (sikur të mos kishit kryer ndonjë operacion).

përgjigje 2:

Dy gjëra krejtësisht të ndryshme. Siguria e fillit nënkupton një funksion që është shkruar në atë mënyrë që të mund të thirret në mënyrë të përsëritur nga shumë fije të ndryshme pa secilën fije të ndërhyrë në funksionin e një fije tjetër (p.sh. duke ndryshuar vlerën nëse një ndryshore ndryshohet nga një tjetër Përdoren fijet).

Do të thotë Atomik (nëse e di se ku po shkoni me të) që do të krijohet një shembull i një objekti. Pavarësisht se sa shpesh është referuar, ky shembull (nga çdo fije) shfaqet gjithmonë.


përgjigje 3:

Operacionet atomike janë një mënyrë për të arritur sigurinë e fillit duke përdorur një lloj bllokimi, të tilla si memeca ose semafora, që përdor operime atomike brenda, ose duke zbatuar sinkronizimin pa bllokime duke përdorur gardhe atomike dhe ruajtëse.

Operacionet atomike mbi llojet e të dhënave primitive janë pra një mjet për të arritur sigurinë e fillit, por nuk garantojnë automatikisht sigurinë e fillit, pasi që zakonisht kryeni operacione të shumta që mbështeten tek njëra-tjetra. Ju duhet të siguroheni që këto operacione kryhen vazhdimisht, p.sh. B. me sinteza.

Po, të shkruani një nga këto llojet e të dhënave atomike në C # është fije e sigurt, por kjo nuk e bën të sigurt funksionin që përdorni në fije. Vetëm siguron që operacioni individual i shkrimit të kryhet si duhet, edhe nëse një fije e dytë e arrin atë "në të njëjtën kohë". Sidoqoftë, herën tjetër që të lexoni nga fillesa aktuale, nuk sigurohet që vlera e shkruar më parë të ruhet, pasi një fije tjetër mund t'i ketë shkruar asaj, por vetëm se vlera e leximit është e vlefshme.