Vítejte na blog.vyvojar.cz Přihlásit | Registrovat | Pomoc

Ars programatica

Udělat dobrý sotware je řemeslo, udělat výjimečný software je umění
Je přiřazení atomická operace?

Zapojil jsem do poněkud vášnivější debaty na téma, zda je v C# přirazení (=) atomická operace, nebo ne. Já jsem toho názoru, že není, protože jsou na jeho provedení potřeba dvě IL instrukce, jiní jsou ovšem toho názoru, že přiřazení atomická operace je, protože v každém okamžiku obsahuje proměnná na levé straně operátoru platnou hodnotu (null v to počítaje).

Co si o tom myslíte vy?

Pro ilustraci příklad zdrojového kódu v C# a příslušného IL kódu (odpovídající si části jsem zvýraznil):

private static void Main()
{
object a = new object();
object b = a;
}

 

.method private hidebysig static void Main() cil managed
{
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
    .entrypoint
    .maxstack 1

    .locals init (

        [0] object a,
        [1] object b)
    L_0000: nop
    L_0001: newobj instance void [mscorlib]System.Object::.ctor()
    L_0006: stloc.0
    L_0007: ldloc.0 
    L_0008: stloc.1 

 
   L_0009: ret
}

Posted: Wednesday, May 28, 2008 9:34 PM by pbouda
Vedeno pod: , ,

Komentář

rmx napsal:

Rekl bych, ze ne nutne, viz. napr, sekci Thread Safety:

http://msdn.microsoft.com/en-us/library/system.datetime.aspx

"Assigning an instance of this type is not thread safe on all hardware platforms because the binary representation of that instance might be too large to assign in a single atomic operation."

# May 28, 2008 10:05 PM

pbouda napsal:

To platí pro value types, na to upozorňoval i jeden v té diskusi. Já jsem ovšem toho názoru, že může za jistých okolností platit i pro třídy.

Nebo jinak: mezi těmi dvěma IL instrukcemi (ld/st) může dojít k přepnutí kontextu, to tom celkem nepochybuji. Otázka ale je, zda to má vliv na atomicitu přiřazení. A tady pořád nevím.

# May 28, 2008 10:30 PM

pazu napsal:

Všechna 32bitová přiřazení jsou atomická.

stloc.1 se totiž přeloží jako jediná instrukce. V tom problém není. Ale výpočet na pravé straně nemusí být atomický. V tvém případě náhodou je - ldloc.0 bude zase jedna instrukce.

Jsou to teda v tvojem případě dvě atomické operace za sebou - výpočet a přiřazení.

int a = 1

int b = a + 1

je taky v pohodě, obě půlky jsou atomické, ale

int a = 1

int x = 2

int b = a + x

výpočet napravo není atomický a správnost pak závisí na tom, zda existuje invariant pro a a x - pokud mezi sebou a a x nemají žádný vztah, pak tu taky nemáme problém; samo přiřazení výsledku už atomické je

long a = 0x1000000000000001;

long b = a

není atomické, jiný thread může vidět klidně i hodnotu b rovnu 1 a tu v programu rozhodně nikde nemáme

# May 28, 2008 10:33 PM

Boris Letocha napsal:

Prirazeni pointeru musi byt atomicka operace jinak by nefungoval garbage collector, mohlo by se stat ze by videl chybny pointer a to by nekonzervativnimu GC neudelalo dobre.

# May 28, 2008 11:05 PM

Petr Vones napsal:

Na urovni MSIL se to moc nevyresi. V pripade viceprocesorovych stroju je ta otazka jeste zajimavejsi :-) Tady uz do jiste miry zalezi na hw architekture, byt se to autori CLR snazili udelat co nejvice "blbovzdorne".

Doporucuji tento clanek: http://blogs.msdn.com/cbrumme/archive/2003/05/17/51445.aspx

# May 29, 2008 12:16 AM

pbouda napsal:

Trošku OT, ale nějak se mi nedaří zformátovat ten ukázkový kód tak, aby se nazalamoval, neutíkal a nebyl zbytenčně mrňavej. Kdybyste na to někdo znal lék, nějaký trik jak na to v Comunity Server, byl bych vám povděčen.

# May 29, 2008 9:02 AM

Rasto napsal:

priradenie je atomicka operacia. len si treba zvolit spravne invarianty.

medzi kazdymi dvoma instrukciami moze nastat context switch. z toho vyplyva, ze invariant, ktory plati po instrukcii I, uz nemusi platit pred instrukciou I+1.

nech p je nejaky smernik.

po instrukcii L0007 plati: p = a.

avsak tato podmienka nemusi platit pred instrukciou L0008.

ale po instrukcii L0008 plati: b = p. A toto je ten hlavny invariant, ktory hovori o tom, ze priradenie je atomicke. Proste sa nestane, ze niektore bity v premennej b su zhodne s p a niektore nie. Vzdy po instrukcii L0008 vieme, comu sa rovna b.

to vsak neznamena, ze po instrukcii L0008 plati, ze b = a. avsak nie preto, ze by sa do b nieco zle priradilo, ale preto, ze a sa mohlo zmenit. ale tento invariant podla mna vobec nie je nutny, aby sme mohli tvrdit, ze priradenie je atomicke.

taktiez si treba uvedomit, ze pred instrukciou L0009 neplati ani invariant: b = p.

teda hovoril som len o smernikoch. lebo velkost smernika je vacsinou rovnaka ako velkost registrov procesora. Takze procesor vie spravit prenos registra do pamate v jednej operacii. Teda mozno existuju nejake zaujimave procesory, kde toto neplati.. ale pre ne asi neexistuje ani JIT .NET kompilator.

# May 29, 2008 10:21 AM

pbouda napsal:

@pazu: Výpočet není z hlediska přiřazení zajímavý, to až výsledek.

@Petr Vones: To je sice pravda, ale to nás nemusí zajímat. Tím virtuálním strojem (v akademickém slova smyslu), na kterém běží C# aplikace, je MSIL. Co je pod ním je už moc hluboko.

Řekl bych, že nejlepší odpověď se objevila v té odkazované diskusi: "Yes, it's atomic. But whether it's "threadsafe" or not in your code is another question. The os won't end up with a corrupt pointer, but your code may end up with a pointer it wasn't expecting. For example, the addition operator itself may be atomic, but "a = a + 4" is not atomic." S tím bych se stotožnil, to je totiž zhruba to, co jsem měl na mysli.

# May 29, 2008 4:18 PM

Petr Vones napsal:

> To je sice pravda, ale to nás nemusí zajímat

To nas naopak musi zajimat. Pokud nebudeme uvazovat prostredi kde bezi soucasne vice threadu, je pak kod vykonavan ciste sekvencne a vsechny operace jsou z tohoto pohledu atomicke. Jenze tak tomu v praxi neni.

Ohledne "Yes, it's atomic. But whether it's "threadsafe" or not in your code is another question" - to je prece naprosty nesmysl.

# May 30, 2008 2:06 PM

pbouda napsal:

@Petr Vones: Vývojáře CLR to samozřejmě musí sakra zajímat, ale proč by to mělo zajímat mně na aplikační úrovni? Jedině kvůli thread-safe, ale zde se stále více kloním k názoru, že to jsou dvě odlišné věci (viz ta vámi rozporovaná citace).

Vidím to tak, že

- přiřazení a = b není thread-safe, protože mezi těmi dvěma instrukcemi může dojít ke context-switch (což je také důvod, proč by vývojář měl mít o IL ponětí),

- ale je atomické, protože proměná na levé straně vždy obsahuje nějakou platnou hodnotu (nemůže se stát, že by nějaké vlákno vidělo půlku bitů ze staré reference a půlku z nové).

# May 30, 2008 3:19 PM
Nejsou povoleny nové komentáře k tomuto příspěvku