Mám rád clánky tohoto typu, i když danou problematiku znám. Ovšem zakončit výklad o vláknech u funkce pthread_create() a nezmínit ani typy vláken.... Je mi jasné, že bude nasledovat druhý clánek o synchronizaci a dalších "pokročilých" tématech, ale myslím, že by se sem vešlo vic.
Pridal bych zminku o direktive __thread u deklarace
promenych. Pokud ji pridate ulozi se promena do zvlastniho segmentu adresovaneho registrem GS. Pri prepinani vlaken se hodnota tohoto registru meni.
Takze vlakno muze mit i "vlastni" promenne na ktere mu
nikdo nemuze sahat. Bohuzel aplikace, ktere k necemu pouzivaly registr GS s tim mohou mit problemy.
Urcite, hlavne proto ze vlakna sdilej pamet, takze nebudes muset saskovat s mezi procesovou komunikaci a budes moct jednodusejc sdilet systemovy prostredky (i kdyz u systemu jako Linux co nema o bezpecnosti v tomhle ani paru je to asi jedno). Na druhou stranu je treba se u vlaken starat o synchronizaci, coz ale asi autorovi moc rikat nebude, jinak by nevypoustel hlasky typu "Pokud nejake vlakno zmeni hodnotu urcite promene, bude tato promenna modifikovana pro vsechna vlakna v procesu" - to bez patricne synchronizace vubec neni pravda, a obzvlast to plati na vice-procesorovych strojich (ale ani na jednom CPU to nemusi platit). A samozrejme na vice CPU vlakna muzou opravdu bezet soucasne, ne jen tak vypadat.
"Pokud nejake vlakno zmeni hodnotu urcite promene, bude tato promenna modifikovana pro vsechna vlakna v procesu" - to bez patricne synchronizace vubec neni pravda
Ale ale, jakto že to není pravda? Tak jak je to obecně napsáno to pravda je. To, že se mohou v určitém okamžiku vyskytovat nekonzistentní stavy, pokud ta proměnná není zapsána atomicky (tzn. není zapsána jednou instrukcí), je jiná věc. Ale to vůbec nemění pravdivost té věty.
Neni to pravda kvuli dvou vecem:
1. Optimizace kompilatoru. Drtiva vetsina kompilatoru dela veci jako cachuje promenny v registrech a pokud ji zmeni jinej thread tak se ta zmena neprojevi. Pak je potreba budto pouzit volatile (pokud je promenna atomicka) anebo synchronizaci (ktera aspon na Win refreshne promenny).
2. CPU cache. Na vice CPU masinach ma kazdy CPU svuj obsah cache kterej neni automaticky synchronizovanej. Pokud jeden CPU zmeni hodnotu pameti kterou ma jinej CPU v cachi tak se ta zmena neprojevi automaticky, ale je opet potreba pouzit synchronizacni funkce, ktery zinvalidujou celou cache anebo atomicky loady (Interlocked[...] funkce ve Win32 API) anebo opet volatile keyword, kterym da clovek kompilatoru najevo ze nechce danou promennou cachovat.
O promennejch co nejsou updatovany atomicky sem nemlvuil, to je komplet jina zaludnost vice threadu.
Ad 1. Pouzivani volatile je samozrejme spravne v mistech, kde clovek vi, ze se promenna bude sdilet mezi vice vlakny takovym zpusobem, ze by jeji podrzeni v registrech zpusobilo problemy; pouziti volatile ale nezbavuje nutnosti synchronizovat a neznamena, ze se promenna pouzije atomicky!
Ad 2. Synchronizace CPU cache je vec systemove architektury, nicmene kazda architektura, ktera si narokuje oznaceni multiprocessor, ma podporu hardwaru pro udrzeni koherence cachi.
Konkretne na IA-32 toto funguje automaticky uz od Pentia, ktere pouzivalo MESI protokol (zda se pouziva tento protokol na Intelech stale, to jsem se uz nepidil).
1) Jasne ze neznamena. Ale o tom sme se nebavili, mluvilo se o tom ze kdyz jeden thread updatuje promennou tak to automaticky neznamena ze se ta zmena projevi i v ostatnich threadech. To je hodne spatny zjednoduseni situace ktery zacatecnikum (a pro ne je tento serial urceny) prinese hodne problemu az se budou snazit najit chyby co se objevujou naprosto nahodne a nejdou debugovat.
2) Minimalne X-86 architektura tu synchronizaci nema automatickou. Je ji potreba explicitne vyvolat, coz opet pokud zacatecnik nevi povede k nepochopitelnym chybam v programu.
nastanou, kdyz pouzivate na SGI Onyx knihovnu OpenGL Performer. To se pak nebudete stacit divit. Po mesicich ladeni jsme nakonec museli "zpatky na stromy" a predelat vse na viceprocesorovou sdilenou pamet :(
Vyse zmineny problem s koherenci cache zde nabyval obrich rozmeru, kdyz v systemu byla pamet fyzivky rozdelena do 6 dvouprocesorovych karet. propojenych ccNuma...