Les fonctions d'attente ou de syncrhonisation

 

 

 

Les fonctions "d'attente" permettent à un thread de passer "en sommeil", jusqu'à ce qu'un état signalé parvienne. Le thread étant en sommeil durant toute la période où l'état n'est pas signalé, on obtient de cefait de très bons résultats au niveau performance. Cet état signalé peut être émis par:

- La fin d'un thread. Le thread signale alors qu'il est "mort".

- Un mutex qui est libre, c'est à dire que le dernier thread qui l'utilisait vient de le libérer.

- Un sémaphore est libre, idem que pour le mutex.

- Qu'un événement est envoyé

- Qu'un timer est déclenché

- Et encore bien d'autres choses...

 

Les fonctions sont:

WaitForSingleObject()

Cette fonction va attendre un état signalé d'un objet pendant un certain laps de temps. Elle rend ensuite la main au thread soit par réception de cet état signalé, soit pour cause de time out.

Remarque : il existe aussi WaitForSingleObjectEx ()

DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);

Les paramètres:

hHandle : Handle de l'objet dont on attend l'état signalé.
dwMilliseconds : Durée maximum d'attente avant time out... Si cette durée est à 0, la fonction va tester l'état et sortir immédiatement. Si la durée est INFINITE, seul l'état signalé provoquera la fin de cette fonction.

Valeur en sortie:

Valeur Désignation
WAIT_ABANDONED Retourné lorsque un mutex utilisé par un thread n'est pas indiqué comme signalé suite à un release, mais par la terminaison du thread qui était propriétaire du mutex.
WAIT_OBJECT_0 Réception de l'état signalé pour l'objet indiqué par hHandle.
WAIT_TIMEOUT Time out, bien entendu, cela implique que l'objet n'est pas signalé !
WAIT_FAILED Il y a eu un problème, il faut utiliser GetLastError...

Remarque : déconseiller d'utiliser cette fonction si le thread est un thread qui gère une fenêtre. Certains messages (dont ceux du système) pourraient ne plus être traités. Préférez MsgWaitForMultipleObjects() ou MsgWaitForMultipleObjectsEx().


WaitForMultipleObjects()

Cette fonction va attendre un état signalé d'au moins un objet ou de tous les objets et ceci pendant un certain laps de temps. Elle rend ensuite la main au thread soit par réception de cet état signalé, soit pour cause de time out.

Remarque : il existe aussi WaitForMultipleObjectsEx()

DWORD WaitForMultipleObjects(
DWORD nCount,
const HANDLE* lpHandles,
BOOL bWaitAll,
DWORD dwMilliseconds
);

Les paramètres:

nCount : Nombre d'objets pour lesquels on veut attendre un état signalé. ( La valeur maximal d'objets que l'on peut indiquer est définie dans MAXIMUM_WAIT_OBJECTS)
lpHandles : Pointeur sur un tableau contenant les handles des objets dont on attend un état signalé. Les objets peuvent être différent entre eux, mais il faut bien vérifier qu'il y a unicité des handles dans ce tableau.
bWaitAll : booléen qui va permettre d'attentre l'état signalé d'au moins un objet de la liste (FALSE) ou de tous les objets de la liste (TRUE).
[in] If this parameter is TRUE, the function returns when the state of all objects in the lpHandles array is signaled. If FALSE, the function returns when the state of any one of the objects is set to signaled. In the latter case, the return value indicates the object whose state caused the function to return.
dwMilliseconds : Durée maximum d'attente avant time out... Si cette durée est à 0, la fonction va tester l'état et sortir immédiatement. Si la durée est INFINITE, seul l'état signalé provoquera la fin de cette fonction.

Les valeurs possible en sortie sont:

Valeur en sortie, si bWaitAll = TRUE

Valeur Désignation
WAIT_ABANDONED_0 à WAIT_ABANDONED_0 + nCount – 1 Retourné lorsque la plupart des objets sont dans un état signalé, mais au moins un mutex utilisé par un thread n'est pas indiqué comme signalé suite à un release, mais par la terminaison du thread qui était propriétaire du mutex.
WAIT_OBJECT_0 à WAIT_OBJECT_0 + nCount – 1 Réception de l'état signalé pour tous les objets indiqués dans lpHandles.
WAIT_TIMEOUT Time out...
WAIT_FAILED Il y a eu un problème, il faut utiliser GetLastError...

Valeur en sortie, si bWaitAll = FALSE

Valeur Désignation
Valeur - WAIT_ABANDONED_0 Index sur le tableau lpHandles pour indiquer qu'un mutex utilisé par un thread n'est pas indiqué comme signalé suite à un release, mais par la terminaison du thread qui était propriétaire du mutex dont on trouvera le handle dans le tableau.
Valeur - WAIT_OBJECT_0 Index sur le tableau lpHandles pour indiquer la réception de l'état signalé d'au moins un objet indiqué dans lpHandles.
WAIT_TIMEOUT Time out...
WAIT_FAILED Il y a eu un problème, il faut utiliser GetLastError...

Remarque, si plusieurs objets peuvent répondre à un WAIT_ABANDONED_0 ou un WAIT_OBJECT_0, l'index retourné est le premier (dans l'ordre croissant) qui répond aux critères.

Remarque : déconseiller d'utiliser cette fonction si le thread est un thread qui gère une fenêtre. Certains messages (dont ceux du système) pourraient ne plus être traités. Préférez MsgWaitForMultipleObjects() ou MsgWaitForMultipleObjectsEx().

 

MsgWaitForMultipleObjects()

Cette fonction est identique à WaitForMultipleObjects: Elle peut donc sortir de son attente par un état signalé ou time out, mais peut aussi sortir suite à la réception d'un certain type de message:

Il existe aussi MsgWaitForMultipleObjectsEx()

DWORD MsgWaitForMultipleObjects(
DWORD nCount,
const HANDLE* pHandles,
BOOL bWaitAll,
DWORD dwMilliseconds,
DWORD dwWakeMask
);

Tous les champs sont identiques à ceux expliqués dans la fonction précédente.

La nouveauté:

dwWakeMask : Indiquez ici le type de message pour lequel (lesquels, car on peut les combiner) la fonction doit sortir de son attente:

Valeur Désignation
QS_ALLEVENTS Messages types QS_INPUT, QS_POSTMESSAGE, QS_TIMER, QS_PAINT, and QS_HOTKEY
QS_ALLINPUT Combinaison de QS_INPUT, QS_POSTMESSAGE, QS_TIMER, QS_PAINT, QS_HOTKEY et QS_SENDMESSAGE
QS_ALLPOSTMESSAGE A la réception de celui-ci, il faudra traiter les messages par GetMessage ou PeekMessage sans utiliser les filtres. Sinon, le wait suivant resortira immédiatement.
QS_HOTKEY Un message de type WM_HOTKEY ou un message INPUT
QS_KEY Un message WM_KEYUP, WM_KEYDOWN, WM_SYSKEYUP, WM_SYSKEYDOWN, WM_MOUSEMOVE ou les messages des boutons de la souris (comme WM_LBUTTONUP, WM_RBUTTONDOWN )
QS_MOUSE Combinaison de QS_MOUSEMOVE et QS_MOUSEBUTTON
QS_MOUSEBUTTON Tous types de messages concernant les boutons de la souris
QS_MOUSEMOVE Message WM_MOUSEMOVE
QS_PAINT Message WM_PAINT
QS_POSTMESSAGE Contrairement à QS_ALLPOSTMESSAGE, la fonction ne ressortira pas s'il existe encore des messages non traités par l'utilisation de filtre.
QS_SENDMESSAGE Message envoyé par un thread
QS_TIMER Message WM_TIMER

En sortie:

Voir WaitForMultipleObjects()

Une valeur supplémentaire : WAIT_OBJECT_0 + nCount indiquant alors que la fonction est sortie par réception d'un message.

 

 

Bon, maintenant les exemples:

Nous avions vu dans le chapitre sur les multithreads qu'il existait différentes manières de terminer un thread, mais quelque soit la solution utilisée, il était fortement consillé d'attendre la terminaison effective du thread:

On reprend donc le programme, mais cette fois-ci, on va attendre effectivement la fin du thread...

Image non trouvée !

 

Une synchronisation sur les mutex, déjà vu dans le chapitre sur les mutex:

Image non trouvée !