C #: Quina diferència hi ha entre seguretat i atòmica?


Resposta 1:

Els mitjans sense seguretat no es desordenen quan hi accedeix des de diversos fils; atòmic significa indivisible, en aquest context equivalent a ininterromput.

Per implementar panys, teniu dues opcions:

  1. Tingueu suport de maquinari per a operacions atòmiques: instruccions composites especials que s’executen en conjunt, com Test -and-set.Be intel·ligent (i pateix les conseqüències) - L’algorisme de Peterson.

A l'exemple dels detalls, tots dos no són segurs; Si he entès correctament, vols dir una cosa així:

classe pública No segur
{
    objecte privat ulock = objecte nou ();

    public int Unsafe1 {get; conjunt; } = 0;

    private int _unsafe2 = 0;
    int públic No insegur2
    {
        aconseguir
        {
            pany (ulock)
            {
                retornar _unsafe2;
            }
        }

        conjunt
        {
            pany (ulock)
            {
                _unsafe2 = valor;
            }
        }
    }
}

Codi de prova:

var u = new No segur ();

Paral·lel.Per (0, 10000000, _ => {u.Unsafe1 ++;});
Paral·lel.Per (0, 10000000, _ => {u.Unsafe2 ++;});

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

Resultat (un dels molts possibles):

4648265 - 4149827

Per als dos, més de la meitat de les actualitzacions van desaparèixer.

El motiu és que ++ no és atòmic, sinó que es tracta de tres operacions separades:

  1. Aconsegueix valor.Afegeix 1 al valor. Valor.

Ho podem arreglar fent una operació incrementada que sigui atòmica. Hi ha moltes maneres de fer-ho, però hi ha dues:

classe pública Segura
{
    objectock privat slock = objecte nou ();

    public int Safe1 {obtenir; conjunt; }
    public void SafeIncrement1 ()
    {
        pany (ulock)
        {
            això.Safe1 ++;
        }
    }

    int privat _safe2 = 0;
    public int Safe2
    {
        aconseguir
        {
            retornar _safe2;
        }
        conjunt
        {
            _safe2 = valor;
        }
    }
    public void SafeIncrement2 ()
    {
        Interlocked.Increment (ref _safe2);
    }
}

Codi de prova:

var s = new Safe ();

Paral·lel.Per (0, 10000000, _ => {s.SafeIncrement1 ();});
Paral·lel.Per (0, 10000000, _ => {s.SafeIncrement2 ();});

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

Els resultats són correctes en ambdós casos. El primer només fa un bloqueig al voltant de tota l'operació composta ++, mentre que el segon utilitza el suport del maquinari per a les operacions atòmiques.

Tingueu en compte que la segona variant anterior, amb Interlocked.Increment, és molt més ràpida, però és realment inferior i limitat pel que pot fer fora de la caixa; Tanmateix, les operacions del paquet interblocat es poden utilitzar per implementar:

  1. Els panys familiars, anomenats "concurrència pessimista" perquè suposen que l'operació s'interromp, per tant, no us molesteu en començar fins que no hagin adquirit algun recurs compartit. "Codi lliure de bloqueig", per exemple, "concurrència optimista" - utilitzant, per exemple, Compara i canvia, utilitza un valor "canari" especial que enregistra al principi, i després assegureu-vos que no ha canviat sota vosaltres al final; la idea és que si apareix un altre fil, aquest matarà el canari, de manera que saps tornar a intentar la teva transacció des del primer moment. Això requereix que el vostre propi codi sigui atòmic, a més, no podeu escriure resultats intermedis a l'estat compartit, heu de triomfar completament o fallar completament (com si no hagueu realitzat operacions).

Resposta 2:

Dues coses completament diferents. Seguretat de fils significa una funció escrita de manera que pot ser anomenada repetidament per molts fils diferents, sense que cada fil envia el funcionament d’un altre fil (per exemple, canviant el valor si una variable que està utilitzant un altre fil)

Atòmic vol dir (si arribo cap a on vas amb això) la creació d’una instància d’un objecte, així que, per molt que es faci referència, sempre veus aquella instància (de qualsevol fil)


Resposta 3:

Les operacions atòmiques són una manera d’aconseguir la seguretat del fil, ja sigui mitjançant algun tipus de panys com Mutexes o Semàfors que utilitzen operacions atòmiques internament o implementant sincronització lliure de bloqueig mitjançant tanques atòmiques i memòria.

Per tant, les operacions atòmiques en tipus de dades primitives són una eina per assolir la seguretat del fil, però no asseguren la seguretat del fil automàticament perquè normalment teniu múltiples operacions que confien les unes en les altres. Heu d'assegurar-vos que aquestes operacions es realitzen sense interrupcions, per exemple, fent servir Mutexes.

Sí, escriure un d'aquests tipus de dades atòmiques a c # no és segur per a fils, però això no fa que la funció que els utilitzeu sigui segura per a fils. Només s’assegura que l’escriptura única s’executi correctament, fins i tot si un segon fil hi accedeix "al mateix temps". Mai menys, la següent lectura del fil actual no està garantida per obtenir el valor escrit anteriorment com un fil diferent podria haver-lo escrit, només que el valor llegit sigui vàlid.