Les évènements

 

Les évènements peuvent servir dans Windows à synchroniser des tâches. Imaginons qu'un thread est besoin d'attendre qu'un autre thread est terminer de s'initialiser avant de commancer son traitement.

Une solution consistera alors à utiliser les évènements, le thread va attendre l'évènement qui ne sera envoyé par le second thread que lorsqu'il aura fini de s'initialiser !

Pour attendre un évènement, on utilisera les fonctions d'attente tel que WaitForSingleObject().

 

Bon, si vous avez vu les mutex, les sémaphoires, et bien les fonctions y ressemblent fort...

On peut d'abord créer/ouvrir un évènement:

HANDLE CreateEvent(
   LPSECURITY_ATTRIBUTES lpEventAttributes,
   BOOL bManualReset,
   BOOL bInitialState,
   LPCTSTR lpName
   );

Les paramètres

lpEventAttributes pointe : pointeur sur une structure de type SECURITY_ATTRIBUTES pour déterminer si l'évènement pourra être hérité ou non par des processus enfants. Si NULL, pas d'héritage possible.

bManualReset : Si TRUE, alors l'évènement est à réinitialiser manuellement grâce à la fonction ResetEvent. Seule cette fonction permettra de faire passer un évènement de l'état signalé à un état non signalé. A FALSE, l'évènement sera automatiquement passé à non signalé à partir du moment où 1 thread détecte cet évènement. Attention donc si plusieurs threads sont attentes !

bInitialState : Si TRUE, l'évènement sera créé intialement à l'état signalé. FALSE, il sera initialisé à non signalé.

lpName : Pointeur sur une chaîne de caractères indiquant le nom de l'évènement. La taille maxi. du nom correspondant là aussi à MAX_PATH. Attention, la comparaison des nom est case sensitive ! Si un évènement existe déjà avec ce nom, les paramètres bManualReset ainsi que bInitialState seront ignorés. On peut créer un évènement sans nom (NULL). Si vous utilisez un nom déjà utilisé pour un mutex, semaphore, ..., alors la fonction retournera une erreur et GetLastError() sera à ERROR_INVALID_HANDLE.

On pourra là aussi préfixer le nom par "Global\" ou "Local\" Pour travailler sur la session globale ou nommé.

En sortie:

Si la fonction n'échoue pas, la valeur est la valeur du handle. Si l'évènement existait déjà avec ce nom, alors la fonction retourne le handle, et GetLastError() retourne ERROR_ALREADY_EXISTS.

Sinon, la fonction retourne 0 si erreur durant la création.

 

Ou simplement ouvrir:

HANDLE OpenEvent(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);

Les paramètres:

dwDesiredAccess : Type d'accès
bInheritHandle: Si à TRUE, un process créé par CreateProcess pourra hériter de l'évènement.
lpName: Le nom comme pour la création.

En sortie:

La fonction retourne un handle ou 0 si problème.

Pour clôturer l'utilisation de l'évènement:

On utilisera la fonction CloseHandle().

 

Changer les états d'un évènement:

Marquer un évènement comme signalé:

BOOL SetEvent(
HANDLE hEvent
);

Marquer un évènement comme non signalé:

BOOL ResetEvent(
HANDLE hEvent
);

Jamais essayé, et appriori à éviter:

Pour passer à un état signalé un évènement et repositionner à non signalé dès que les treads l'on tous traités...

BOOL PulseEvent(
HANDLE hEvent
);

 

Voici un exemple tout bête, un thread va plus vite que le second pour s'initialiser, il va donc attendre la fin de l'initialisation du second avant de continuer (dans notre cas, pour se terminer...):

Image non trouvée !