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:
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...
Une synchronisation sur les mutex, déjà vu dans le chapitre sur les mutex: