660 likes | 827 Views
Synchronisation. Objectif du chapitre. Création d’une application ayant plusieurs threads Synchronisations entre threads Section critique Mutex Événement Sémaphore Exemples pratiques. Synchronisation.
E N D
Synchronisation Synchronisation
Objectif du chapitre • Création d’une application ayant plusieurs threads • Synchronisations entre threads • Section critique • Mutex • Événement • Sémaphore • Exemples pratiques Synchronisation
Synchronisation • Nous avons vu comment créer un thread dans un processus dans le chapitre précédent. Nous poursuivons maintenant avec plusieurs threads dans un même processus. • Dans un premier exemple, “NOSYNC”, nous allons faire apparaître une difficulté lorsque plusieurs threads s’exécutent simultanément. • Dans les exemples suivants, nous travaillerons sur plusieurs solutions au problème. Synchronisation
Création de 2 Threads • Créer un projet NOSYNC • Un process va créer 2 threads fils A et B • Ces threads font juste l’impression de messages • Début de thread • Texte • Fin de thread • La demande d’impression d’un texte fait que le thread perd la main Synchronisation
NOSYNC main (1) #include "stdafx.h" DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { HANDLE H1,H2; Synchronisation
NOSYNC main (2) printf("debut du main NOSYNC\n"); H1=CreateThread(0, 0, THREAD_A, 0, 0, 0); H2=CreateThread(0, 0, THREAD_B, 0, 0, 0); Sleep(5000); CloseHandle(H1); CloseHandle(H2); printf("fin du main NOSYNC\n"); getchar(); return 0; } Synchronisation
NOSYNC Thread_A DWORD WINAPI THREAD_A(LPVOID p) { printf("debut du thread A\n"); printf("le loup et "); printf("\nl'agneau "); printf("fin du thread A\n"); return 0; } Synchronisation
NOSYNC Thread_B DWORD WINAPI THREAD_B(LPVOID p) { printf("debut du thread B\n"); printf("la cerise"); printf("sur le gateau\n"); printf("fin du thread B\n"); return 0; } Synchronisation
Résultat de l’exécution Synchronisation
Synchronisation • Les messages sont mélangés • Il faut « synchroniser » l’exécution des threads, c’est-à-dire, dans notre exemple, attendre qu’un message soit terminé avant d’imprimer l’autre • Synchronisation possible par • Section critique • Mutex • Événement (Event) • Sémaphore Synchronisation
Section critique : types • Les types « section critique » et « pointeur sur section critique » sont définis par des typedef CRITICAL_SECTION cs; LPCRITICAL_SECTION lpcs; • Cela permet des contrôles par le compilateur et contribue à éviter un usage inapproprié Synchronisation
Section critique : fonctions (1) • Initialisation d’une section critique void InitializeCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); • Entrée en section critique void EnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); • Sortie d’une section critique void LeaveCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); Synchronisation
Section critique : fonctions (2) • Restitution des ressources void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); • Variante d’entrée non bloquante BOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection ); Synchronisation
Section critique : application • Créer une application CRITIC qui imprime correctement les deux messages • Déclaration dans main ou en variable globale d’une variable section critique • Utilisation de cette variable dans thread_A et thread_B pour contrôler l’impression des messages Synchronisation
CRITIC main (1) #include "stdafx.h" DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { CRITICAL_SECTION cs; HANDLE H1,H2; Synchronisation
CRITIC main (2) InitializeCriticalSection(&cs); printf("debut du main CRITIC\n"); H1=CreateThread( 0, 0, THREAD_A, &cs, 0, 0); H2=CreateThread( 0, 0, THREAD_B, &cs, 0, 0); Sleep(5000); CloseHandle(H1); CloseHandle(H2); DeleteCriticalSection(&cs); printf("fin du main CRITIC\n"); getchar(); return 0; } Synchronisation
CRITIC thread_A DWORD WINAPI THREAD_A(LPVOID pCriticSec) { printf("debut du thread A\n"); EnterCriticalSection( (LPCRITICAL_SECTION)pCriticSec); printf("THREAD_A: le loup et "); printf("l'agneau\n"); LeaveCriticalSection( (LPCRITICAL_SECTION)pCriticSec); printf("fin du thread A\n"); return 0; } Synchronisation
CRITIC thread_B DWORD WINAPI THREAD_B(LPVOID pCriticSec) { printf("debut du thread B\n"); EnterCriticalSection((LPCRITICAL_SECTION)p CriticSec); printf("THREAD_B: la cerise "); printf("sur le gateau\n"); LeaveCriticalSection((LPCRITICAL_SECTION)p CriticSec); printf("fin du thread B\n"); return 0; } Synchronisation
Résultat de l’exécution Synchronisation
Mutex • Mutex : raccourci pour mutual exclusion • Objet système destiné à gérer les synchronisations par exclusion mutuelle • Synchronisation • Intra-processus • Inter-processus • Alloué au plus à un thread à un instant donné Synchronisation
Mutex : fonctions (1) • Création d’un Mutex HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCTSTR lpName ); lpMutexAttributes : NULL pour CE bInitialOwner : prise ou non du mutex lpName : pointeur sur un nom ou NULL valeur retournée : création ou non du mutex, etc. Synchronisation
MUTEX : fonctions (2) • Attente de disponibilité du mutex DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); hHandle : handle du mutex dwMilliseconds : INFINITE (pas de time-out) valeur de retour : état du mutex • Libération du mutex BOOL ReleaseMutex(HANDLE hMutex );valeur de retour : réussite ou non Synchronisation
MUTEX : fonctions (2) • Attente de disponibilité du mutex DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); hHandle : handle du mutex dwMilliseconds : INFINITE (pas de time-out) valeur de retour : état du mutex • Libération du mutex BOOL ReleaseMutex(HANDLE hMutex );valeur de retour : réussite ou non Synchronisation
Application MUTEX • Créer une application MUTEX • Utiliser les mutexes pour que les textes ne soient plus mélangés lors de l’impression Synchronisation
MUTEX main (1) #include "stdafx.h" DWORD WINAPI THREAD_A(HANDLE hMutex); DWORD WINAPI THREAD_B(HANDLE hMutex); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { HANDLE hMutex,H1,H2; printf("debut du main MUTEX\n"); hMutex=CreateMutex(NULL,FALSE,NULL); Synchronisation
MUTEX main (2) H1=CreateThread(0,0,THREAD_A,hMutex,0,0); H2=CreateThread(0,0,THREAD_B,(LPVOID)hMutex,0,0); Sleep(5000); CloseHandle(H1); CloseHandle(H2); CloseHandle(hMutex); printf("fin du main MUTEX\n"); getchar(); return 0; } Synchronisation
MUTEX thread_A DWORD WINAPI THREAD_A(HANDLE hMut) { printf("debut du thread A\n"); WaitForSingleObject(hMut,INFINITE); printf("THREAD_A: le loup et "); printf("l'agneau\n"); ReleaseMutex(hMut); printf("fin du thread A\n"); return 0; } Synchronisation
MUTEX Thread_B DWORD WINAPI THREAD_B(HANDLE hMut) { printf("debut du thread B\n"); WaitForSingleObject(hMut,INFINITE); printf("THREAD_B: la cerise "); printf("sur le gateau\n"); ReleaseMutex(hMut); printf("fin du thread B\n"); return 0; } Synchronisation
Résultat de l’exécution Synchronisation
Synchronisation par événement • Autre forme de synchronisation plus souple que par les mutex • Gestion plus riche des événements • Création • Prise de possession • Restitution • Transmission de données • Ouvertures multiples Synchronisation
Événements : fonctions (1) • Création d’un événementHANDLE CreateEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPTSTR lpName ); lpEventAttributes: NULL pour CE bManualReset: TRUE autorise ResetEvent bInitialState: TRUE événement disponible lpName: NULL pour un événement sans nom Synchronisation
Événements : fonctions (2) • Ouverture d’événementHANDLE OpenEvent(DWORD dwDesiredAcess, BOOL bInheritHandle, LPCTSTR lpName); • Fournit un handle sur un événement déjà créé par CreateEvent • Correspondance par le nom lpName Synchronisation
Événements : fonctions (3) • Attente d’événementDWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); • Activation de l’événement BOOL SetEvent(HANDLE hEvent); • Désactivation de l’événement BOOL ResetEvent(HANDLE hEvent); Synchronisation
Événements : fonctions (4) • Dépôt d’une donnéeBOOL SetEventData(HANDLE hEvent, DWORD dwData);hEvent : handle de l’événementdwData : donnée à affecter • Récupération de la donnée déposée DWORD GetEventData(HANDLE hEvent); valeur de retour : la donnée déposée Synchronisation
Application EVENT • Créer une application EVENT • EventA est créé actif dans le thread A pour autoriser la tâche A à démarrer • EventB est créé inactif dans le thread B • La tâche A désactivera EventA en début de tâche et activera EventB en fin de tâche • La tâche B désactivera EventB en début de tâche et réactivera EventA en fin de tâche • Les 2 tâches vont s’activer mutuellement Synchronisation
EVENT main (1) #include "stdafx.h" DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); HANDLE hEventA,hEventB; //variables globales pour… int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { HANDLE H1,H2; printf("debut du main EVENT\n"); Synchronisation
EVENT main (2) hEventA=CreateEvent(NULL,TRUE,TRUE,NULL); hEventB=CreateEvent(NULL,TRUE,FALSE,NULL); H1=CreateThread(0,0,THREAD_A,0,0,0); H2=CreateThread(0,0,THREAD_B,0,0,0); Sleep(5000); CloseHandle(H1); CloseHandle(H2); CloseHandle(hEventA); CloseHandle(hEventB); printf("fin du main EVENT\n"); getchar(); return 0; } Synchronisation
EVENT THREAD_A DWORD WINAPI THREAD_A(LPVOID p) { printf("debut du thread A\n"); WaitForSingleObject(hEventA,INFINITE); printf("THREAD_A: le loup et "); printf("l'agneau\n"); ResetEvent(hEventA); SetEvent(hEventB); printf("fin thread A\n"); return 0; } Synchronisation
EVENT THREAD_B DWORD WINAPI THREAD_B(LPVOID p) { printf("debut thread B\n"); WaitForSingleObject(hEventB,INFINITE); printf("THREAD_B: la cerise "); printf("sur le gateau\n"); ResetEvent(hEventB); SetEvent(hEventA); printf("fin thread B\n"); return 0; } Synchronisation
Résultat de l’exécution Synchronisation
Application EVENT_NOM • L’application est du même style que EVENT, mais montre l’usage d’autres fonctionnalités : • Événements nommés • Passage de donnée • Utilisation d’un dépassement de délai • Le thread imprime un message ou un autre suivant la donnée fournie, 1 ou 2 Synchronisation
EVENT_NOM : main (1) // EVENT_NOM.cpp : Defines the entry point for the… #include "stdafx.h" #include "Pkfuncs.h"//pour les fonctions SetEventData… DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) Synchronisation
EVENT_NOM : main (2) { HANDLE H1,H2; HANDLE hEventA,hEventB; LPTSTR pNomEventA={L"NOM_EVENT_A"}; LPTSTR pNomEventB={L"NOM_EVENT_B"}; printf("debut du main EVENT_NOM\n"); hEventA=CreateEvent(NULL,TRUE,TRUE,pNomEventA); SetEventData(hEventA,1); hEventB=CreateEvent(NULL,TRUE,FALSE,pNomEventB); H1=CreateThread(0,0,THREAD_A,0,0,0); H2=CreateThread(0,0,THREAD_B,0,0,0); Synchronisation
EVENT_NOM : main (3) Sleep(5000); CloseHandle(H1); CloseHandle(H2); CloseHandle(hEventA); CloseHandle(hEventB); printf("fin du main EVENT_NOM\n"); getchar(); return 0; } Synchronisation
EVENT_NOM : THREAD_A (1) DWORD WINAPI THREAD_A(LPVOID p) { DWORD dwData_A; DWORD dwTime_out=1000; HANDLE hEvent_A,hEvent_B; LPTSTR pNomEventA={L"NOM_EVENT_A"}; LPTSTR pNomEventB={L"NOM_EVENT_B"}; printf("debut du thread A\n"); hEvent_A=OpenEvent(EVENT_ALL_ACCESS,FALSE,pNomEventA); hEvent_B=OpenEvent(EVENT_ALL_ACCESS,FALSE,pNomEventB); Synchronisation
EVENT_NOM : THREAD_A (2) while(WAIT_OBJECT_0==WaitForSingleObject(hEvent_A,dwTime_out)) { dwData_A=GetEventData(hEvent_A); printf("THREAD_A: fable %d : ",dwData_A); switch (dwData_A) { case 1 : printf("le loup "); printf("et l'agneau\n"); break; case 2 : printf("le lion et "); printf("le rat\n"); break; default : break; } Synchronisation
EVENT_NOM : THREAD_A (3) ResetEvent(hEvent_A); SetEvent(hEvent_B); } printf("fin thread A\n"); return 0; Synchronisation
EVENT_NOM : THREAD_B (1) DWORD WINAPI THREAD_B(LPVOID p) { HANDLE hEvent_A,hEvent_B; LPTSTR pNomEventA={L"NOM_EVENT_A"}; LPTSTR pNomEventB={L"NOM_EVENT_B"}; printf("debut thread B\n"); hEvent_A=OpenEvent(EVENT_ALL_ACCESS,FALSE,pNomEventA); SetEventData(hEvent_A,2); hEvent_B=OpenEvent(EVENT_ALL_ACCESS,FALSE,pNomEventB); WaitForSingleObject(hEvent_B,INFINITE); Synchronisation
EVENT_NOM : THREAD_B (2) printf("THREAD_B: la cerise "); printf("sur le gateau\n"); ResetEvent(hEvent_B); SetEvent(hEvent_A); Sleep(2000); //suivant valeur : fin de A avant B ou de B avant A printf("fin thread B\n"); return 0; } Synchronisation
Résultat de l’exécution Synchronisation