Multithreading: Použití synchronizačních tříd MFC
Synchronizace přístupu k prostředkům mezi vlákny je běžným problémem při psaní vícevláknových aplikací. Pokud mají dvě nebo více vláken současně přístup ke stejným datům, může to vést k nežádoucím a nepředvídatelným výsledkům. Například jedno vlákno může aktualizovat obsah struktury, zatímco jiné vlákno čte obsah stejné struktury. Není známo, jaká data vlákno pro čtení obdrží: stará data, nově zapsaná data či kombinaci obou. MFC poskytuje řadu synchronizačních a synchronizačních tříd přístupu, které vám pomůžou tento problém vyřešit. Toto téma vysvětluje třídy, které jsou k dispozici a jak je použít k vytvoření tříd bezpečných pro vlákna v typické vícevláknové aplikaci.
Typická vícevláknová aplikace má třídu, která představuje prostředek, který se má sdílet mezi vlákny. Správně navržená, plně bezpečná třída vláken nevyžaduje volání žádných synchronizačních funkcí. Vše se zpracovává interně do třídy, takže se můžete soustředit na to, jak nejlépe používat třídu, a ne o tom, jak může dojít k poškození. Efektivní technikou pro vytvoření plně bezpečné třídy vlákna je sloučení synchronizační třídy do třídy prostředků. Sloučení synchronizačních tříd do sdílené třídy je jednoduchý proces.
Jako příklad vezměte aplikaci, která udržuje propojený seznam účtů. Tato aplikace umožňuje zkoumat až tři účty v samostatných oknech, ale v každém konkrétním okamžiku je možné aktualizovat pouze jeden. Při aktualizaci účtu se aktualizovaná data posílají přes síť do archivu dat.
Tato ukázková aplikace používá všechny tři typy tříd synchronizace. Vzhledem k tomu, že umožňuje prozkoumání až tří účtů najednou, používá CSemaphore k omezení přístupu na tři objekty zobrazení. Když dojde k pokusu o zobrazení čtvrtého účtu, aplikace buď počká, dokud se jedno z prvních tří oken nezavře nebo se nezdaří. Při aktualizaci účtu aplikace používá CCriticalSection k zajištění, že se současně aktualizuje jenom jeden účet. Po úspěšném dokončení aktualizace signalizuje CEvent, které uvolní vlákno čekající na signál události. Toto vlákno odešle nová data do archivu dat.
Návrh třídy bezpečné pro přístup z více vláken
Pokud chcete, aby třída byla plně bezpečná pro přístup z více vláken, nejprve přidejte příslušnou synchronizační třídu do sdílených tříd jako datového člena. V předchozím příkladu CSemaphore
správy účtů by byl datový člen přidán do třídy zobrazení, CCriticalSection
datový člen by byl přidán do třídy propojeného seznamu a CEvent
datový člen by byl přidán do třídy úložiště dat.
Dále přidejte volání synchronizace do všech členských funkcí, které upravují data ve třídě nebo mají přístup k řízenému prostředku. V každé funkci byste měli vytvořit objekt CSingleLock nebo CMultiLock a volat funkci objektu Lock
. Když objekt zámku zmizí z oboru a zničí se, destruktor objektu za vás zavolá Unlock
a uvolní prostředek. Samozřejmě můžete volat Unlock
přímo, pokud chcete.
Návrh třídy bezpečné pro přístup z více vláken tímto způsobem umožňuje použití v vícevláknové aplikaci stejně snadno jako třída bez vláken, ale s vyšší úrovní bezpečnosti. Zapouzdření objektu synchronizace a přístupového objektu synchronizace do třídy prostředku poskytuje všechny výhody plně bezpečného programování s vlákny bez nevýhod údržby synchronizačního kódu.
Následující příklad kódu ukazuje tuto metodu pomocí datového členu ( m_CritSection
typu CCriticalSection
), deklarovaného ve sdílené třídě prostředků a objektu CSingleLock
. Synchronizace sdíleného prostředku (odvozeného z CWinThread
) se pokouší vytvořit CSingleLock
objekt pomocí adresy objektu m_CritSection
. Provede se pokus o uzamčení prostředku a při získání práce na sdíleném objektu. Po dokončení práce se zdroj odemkne voláním Unlock
.
CSingleLock singleLock(&m_CritSection);
singleLock.Lock();
// resource locked
//.usage of shared resource...
singleLock.Unlock();
Poznámka:
CCriticalSection
, na rozdíl od jiných synchronizačních tříd MFC nemá možnost časově uzamčeného požadavku. Čekání na uvolnění vlákna je nekonečné.
Nevýhodami tohoto přístupu je, že třída bude o něco pomalejší než stejná třída bez přidaných synchronizačních objektů. Pokud existuje také šance, že více než jedno vlákno může objekt odstranit, sloučený přístup nemusí vždy fungovat. V této situaci je lepší udržovat samostatné synchronizační objekty.
Informace o určení třídy synchronizace, která se má použít v různých situacích, naleznete v tématu Multithreading: Kdy použít synchronizační třídy. Další informace o synchronizaci naleznete v tématu Synchronizace v sadě Windows SDK. Další informace o podpoře multithreadingu v prostředí MFC naleznete v tématu Multithreading s C++ a MFC.