Psaní programů s více vlákny pro Win32
Jestliže píšete program s více vlákny, musíte koordinovat jejich chování a využití prostředků programu. Musíte se také ujistit, že každé vlákno obdrží vlastní zásobník.
Sdílení společných prostředků mezi vlákny
Poznámka
Podobné informace z hlediska knihovny MFC naleznete v Multithreading: Programové tipy a Multithreading: Kdy použít synchronizační třídy.
Každé vlákno má svůj vlastní zásobník a své vlastní kopie registrů CPU. Další prostředky, jako jsou například soubory, statická data a paměť haldy, jsou sdíleny všemi vlákny v procesu. Vlákna, která používají tyto společné prostředky, musí být synchronizována. Win32 poskytuje několik způsobů jak synchronizovat zdroje včetně semaforů, kritických oddílů, událostí a mutexů.
Při přístupu více vláken ke statickým datům musí váš program stanovit možné zdroje konfliktů. Zvažte program, kde jedno vlákno aktualizuje statickou datovou strukturu obsahující souřadnice x,y pro položky, které mají být zobrazeny jiným vláknem. Pokud vlákno, které aktualizuje, změní souřadnici x a je přepnuto před tím, než může změnit souřadnici y, vlákno, které zobrazuje, může být provedeno před tím, než je aktualizována souřadnice y. Položka bude zobrazena na nesprávném místě. Tomuto problému se můžete vyhnout pomocí semaforů, které řídí přístup ke struktuře.
Mutex (zkratka pro Vzájemně vyloučený přístup) představuje způsob komunikace mezi vlákny nebo procesy, které jsou vzájemně asynchronně prováděny. Tato komunikace je obvykle používaná ke koordinaci činností s více vlákny nebo procesy, obvykle pomocí řízení přístupu ke sdíleným prostředkům, zamknutím a odemknutím zdroje. Chcete-li vyřešit tento problém s aktualizací souřadnice x,y, vlákno, které aktualizuje, nastaví mutex označující, že datová struktura je používána před provedením aktualizace. To by mělo odstranit mutex po zpracování obou souřadnic. Vlákno, které zobrazuje, musí čekat, než je mutex odstraněn, teprve potom dojde k aktualizaci zobrazení. Tento proces čekání na mutex se často nazývá "blocking on a mutex", protože je proces blokován a nemůže pokračovat, dokud není mutex vymazán.
Program Bounce.c, znázorněný v Ukázkový vícevláknový program v C, používá mutex s názvem ScreenMutex ke koordinaci aktualizací obrazovky. Pokaždé, když je některé z vláken, které zobrazuje, připraveno k zápisu na obrazovku, volá WaitForSingleObject spolu s ScreenMutex a konstantou INFINITE k označení, že volání WaitForSingleObject by mělo čekat na mutex a neměl by vypršet časový limit. Jestliže je ScreenMutex odstraněn, čekací funkce nastaví mutex, takže jiné vlákna nemohou zasahovat do zobrazování a pokračuje v provádění vlákna. V opačném případě vlákno zablokuje, dokud se nevymaže mutex. Poté, co vlákno dokončí aktualizaci zobrazení, uvolní mutex voláním ReleaseMutex.
Zobrazení na obrazovce a statická data jsou pouze dva zdroje vyžadující pečlivé řízení. Program může mít například více vláken, která přístupují ke stejnému souboru. Protože jiné vlákno může přesunout ukazatel na soubor, musí každé vlákno resetovat ukazatel na soubor před čtením nebo zápisem. Kromě toho se každé vlákno musí přesvědčit, že není přepnuto mezi dobou uložení ukazatele a přístupu k souboru. Tyto vlákna by měla použít semafor pro koordinaci přístupu k souboru pomocí "bracketing" každého přístupu k souboru s voláním WaitForSingleObject a ReleaseMutex. Tento postup ukazuje následující příklad kódu:
HANDLE hIOMutex= CreateMutex (NULL, FALSE, NULL);
WaitForSingleObject( hIOMutex, INFINITE );
fseek( fp, desired_position, 0L );
fwrite( data, sizeof( data ), 1, fp );
ReleaseMutex( hIOMutex);
Zásobníky vlákna
Veškerý výchozí prostor v zásobníku aplikace je přidělen prvnímu vláknu, jenž je prováděno a je známé jako vlákno 1. V důsledku toho je třeba určit, kolik paměti má být alokováno pro jednotlivé zásobníky pro každé vlákno programu. V případě potřeby operační systém přidělí další prostor v zásobníku pro vlákno, ale je nutné zadat výchozí hodnotu.
První argument volání _beginthread je ukazatel na funkci BounceProc, která zpracuje vlákna. Druhý argument určuje výchozí velikost zásobníku pro vlákno. Poslední argument je identifikační číslo, které je předáno BounceProc. BounceProc používá ID jako počáteční hodnotu pro generátor pseudonáhodných čísel, aby vybral atribut barvy vlákna a zobrazil znak.
Vlákna, která volají běhovou knihovnu jazyka C nebo rozhraní API systému Win32, musí umožňovat dostatečný prostor v zásobníku pro knihovnu a funkce rozhraní API, jenž volají. Funkce C printf vyžaduje více než 500 bajtů prostoru v zásobníku a při volání rutin rozhraní API systému Win32 byste měli mít k dispozici 2 kB volného prostoru v zásobníku.
Vzhledem k tomu, že každé vlákno má svůj vlastní zásobník, můžete předejít potenciálním kolizím s položkami dat pomocí tak málo statických dat, jak jen to je možné. Navrhněte program tak, aby používal automatické proměnné zásobníku pro všechna data, která mohou být soukromá pro vlákno. Jediné globální proměnné v aplikaci Bounce.c jsou objekty mutex nebo proměnné, které se nikdy nezmění poté, co jsou inicializovány.
Systém Win32 také poskytuje místní úložiště vlákna (Thread-Local Storage (TLS)) k ukládání dat pro vlákno. Další informace naleznete v tématu Místní úložiště vláken (TLS).