Delen via


Multithreading: De MFC-synchronisatieklassen gebruiken

Het synchroniseren van resourcetoegang tussen threads is een veelvoorkomend probleem bij het schrijven van multithreaded toepassingen. Het tegelijkertijd toegang hebben van twee of meer threads tot dezelfde gegevens kan leiden tot ongewenste en onvoorspelbare resultaten. Een thread kan bijvoorbeeld de inhoud van een structuur bijwerken terwijl een andere thread de inhoud van dezelfde structuur leest. Het is onbekend welke gegevens de leesthread ontvangt: de oude gegevens, de nieuw geschreven gegevens of mogelijk een combinatie van beide. MFC biedt een aantal synchronisatie- en synchronisatietoegangsklassen om dit probleem op te lossen. In dit onderwerp worden de beschikbare klassen uitgelegd en hoe u deze kunt gebruiken om threadveilige klassen te maken in een typische multithreaded-toepassing.

Een typische multithreaded-toepassing heeft een klasse die een resource vertegenwoordigt die moet worden gedeeld tussen threads. Voor een correct ontworpen, volledig thread-veilige klasse hoeft u geen synchronisatiefuncties aan te roepen. Alles wordt binnen de klasse afgehandeld, zodat u zich kunt concentreren op hoe u de klasse het beste kunt gebruiken, in plaats van hoe deze kan worden gecorrumpeerd. Een effectieve techniek voor het maken van een volledig thread-veilige klasse is het samenvoegen van de synchronisatieklasse in de resourceklasse. Het samenvoegen van de synchronisatieklassen in de gedeelde klasse is een eenvoudig proces.

Neem bijvoorbeeld een toepassing die een gekoppelde lijst met accounts onderhoudt. Met deze toepassing kunnen maximaal drie accounts in afzonderlijke vensters worden onderzocht, maar er kan slechts één op een bepaald moment worden bijgewerkt. Wanneer een account wordt bijgewerkt, worden de bijgewerkte gegevens via het netwerk naar een gegevensarchief verzonden.

In deze voorbeeldtoepassing worden alle drie de typen synchronisatieklassen gebruikt. Omdat er maximaal drie accounts tegelijk kunnen worden onderzocht, wordt CSemaphore gebruikt om de toegang tot drie weergaveobjecten te beperken. Wanneer een poging om een vierde account weer te geven plaatsvindt, wacht de toepassing totdat een van de eerste drie vensters wordt gesloten of mislukt. Wanneer een account wordt bijgewerkt, gebruikt de toepassing CCriticalSection om ervoor te zorgen dat slechts één account tegelijk wordt bijgewerkt. Nadat de update is voltooid, geeft het CEvent aan, waardoor een thread wordt vrijgegeven die wacht tot de gebeurtenis wordt gesignaleerd. Met deze thread worden de nieuwe gegevens naar het gegevensarchief verzonden.

Een Thread-Safe-klasse ontwerpen

Als u een klasse volledig thread-veilig wilt maken, voegt u eerst de juiste synchronisatieklasse toe aan de gedeelde klassen als gegevenslid. In het vorige voorbeeld van accountbeheer wordt een CSemaphore gegevenslid toegevoegd aan de weergaveklasse, wordt een CCriticalSection gegevenslid toegevoegd aan de klasse gekoppelde lijst en wordt er een CEvent gegevenslid toegevoegd aan de gegevensopslagklasse.

Voeg vervolgens synchronisatie-aanroepen toe aan alle lidfuncties die de gegevens in de klasse wijzigen of toegang hebben tot een beheerde resource. In elke functie moet u een CSingleLock - of CMultiLock-object maken en de functie van Lock dat object aanroepen. Wanneer het vergrendelingsobject buiten het bereik valt en wordt vernietigd, roept de destructor van het object Unlock aan, waardoor de resource wordt vrijgegeven. Natuurlijk kunt u direct bellen Unlock als u wilt.

Door uw thread-veilige klasse op deze manier te ontwerpen, kan deze net zo eenvoudig worden gebruikt in een multithreaded-toepassing als een niet-thread-veilige klasse, maar met een hoger veiligheidsniveau. Het synchronisatieobject en het synchronisatietoegangsobject inkapselen in de klasse van de resource biedt alle voordelen van volledig thread-veilig programmeren zonder het nadeel van het onderhouden van synchronisatiecode.

In het volgende codevoorbeeld wordt deze methode gedemonstreerd met behulp van een gegevenslid ( m_CritSection van het type CCriticalSection), gedeclareerd in de gedeelde resourceklasse en een CSingleLock object. De synchronisatie van de gedeelde resource (afgeleid van CWinThread) wordt geprobeerd door een CSingleLock object te maken met behulp van het adres van het m_CritSection object. Er wordt geprobeerd de resource te vergrendelen en, zodra die is verkregen, wordt er gewerkt aan het gedeelde object. Wanneer het werk is voltooid, wordt de resource ontgrendeld met een aanroep naar Unlock.

CSingleLock singleLock(&m_CritSection);
singleLock.Lock();
// resource locked
//.usage of shared resource...

singleLock.Unlock();

Opmerking

CCriticalSection, in tegenstelling tot andere MFC-synchronisatieklassen, heeft niet de optie van een getimede vergrendelingsaanvraag. De wachttijd voordat een thread vrij wordt, is oneindig.

De nadelen van deze methode zijn dat de klasse iets langzamer is dan dezelfde klasse zonder dat de synchronisatieobjecten zijn toegevoegd. Als er ook een kans bestaat dat meer dan één thread het object kan verwijderen, werkt de samengevoegde benadering mogelijk niet altijd. In deze situatie is het beter om afzonderlijke synchronisatieobjecten te onderhouden.

Zie Multithreading voor informatie over het bepalen welke synchronisatieklasse in verschillende situaties moet worden gebruikt : Wanneer u de synchronisatieklassen gebruikt. Zie Synchronisatie in de Windows SDK voor meer informatie over synchronisatie . Zie Multithreading met C++ en MFC voor meer informatie over ondersteuning voor multithreading in MFC.

Zie ook

Multithreading met C++ en MFC