Synchronisation jcmdlp0106 Synchronisation 1 Objectif du chapitre Cration
Synchronisation jc/md/lp-01/06 Synchronisation 1
Objectif du chapitre • Création d’une application ayant plusieurs threads • Synchronisations entre threads – – Section critique Mutex Événement Sémaphore • Exemples pratiques jc/md/lp-01/06 Synchronisation 2
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. jc/md/lp-01/06 Synchronisation 3
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 jc/md/lp-01/06 Synchronisation 4
NOSYNC main (1) #include "stdafx. h" DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); int WINAPI Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { HANDLE H 1, H 2; jc/md/lp-01/06 Synchronisation 5
NOSYNC main (2) printf("debut du main NOSYNCn"); H 1=Create. Thread(0, 0, THREAD_A, 0, 0, 0); H 2=Create. Thread(0, 0, THREAD_B, 0, 0, 0); Sleep(5000); Close. Handle(H 1); Close. Handle(H 2); printf("fin du main NOSYNCn"); getchar(); return 0; } jc/md/lp-01/06 Synchronisation 6
NOSYNC Thread_A DWORD WINAPI THREAD_A(LPVOID p) { printf("debut du thread An"); printf("le loup et "); printf("nl'agneau "); printf("fin du thread An"); return 0; } jc/md/lp-01/06 Synchronisation 7
NOSYNC Thread_B DWORD WINAPI THREAD_B(LPVOID p) { printf("debut du thread Bn"); printf("la cerise"); printf("sur le gateaun"); printf("fin du thread Bn"); return 0; } jc/md/lp-01/06 Synchronisation 8
Résultat de l’exécution jc/md/lp-01/06 Synchronisation 9
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 jc/md/lp-01/06 Synchronisation 10
Section critique : types • Les types « section critique » et « pointeur 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é jc/md/lp-01/06 Synchronisation 11
Section critique : fonctions (1) • Initialisation d’une section critique void Initialize. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); • Entrée en section critique void Enter. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); • Sortie d’une section critique void Leave. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); jc/md/lp-01/06 Synchronisation 12
Section critique : fonctions (2) • Restitution des ressources void Delete. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); • Variante d’entrée non bloquante BOOL Try. Enter. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); jc/md/lp-01/06 Synchronisation 13
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 jc/md/lp-01/06 Synchronisation 14
CRITIC main (1) #include "stdafx. h" DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); int WINAPI Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { CRITICAL_SECTION cs; HANDLE H 1, H 2; jc/md/lp-01/06 Synchronisation 15
CRITIC main (2) Initialize. Critical. Section(&cs); printf("debut du main CRITICn"); H 1=Create. Thread( 0, 0, THREAD_A, &cs, 0, 0); H 2=Create. Thread( 0, 0, THREAD_B, &cs, 0, 0); Sleep(5000); Close. Handle(H 1); Close. Handle(H 2); Delete. Critical. Section(&cs); printf("fin du main CRITICn"); getchar(); return 0; } jc/md/lp-01/06 Synchronisation 16
CRITIC thread_A DWORD WINAPI THREAD_A(LPVOID p. Critic. Sec) { printf("debut du thread An"); Enter. Critical. Section( (LPCRITICAL_SECTION)p. Critic. Sec); printf("THREAD_A: le loup et "); printf("l'agneaun"); Leave. Critical. Section( (LPCRITICAL_SECTION)p. Critic. Sec); printf("fin du thread An"); return 0; } jc/md/lp-01/06 Synchronisation 17
CRITIC thread_B DWORD WINAPI THREAD_B(LPVOID p. Critic. Sec) { printf("debut du thread Bn"); Enter. Critical. Section((LPCRITICAL_SECTION)p Critic. Sec); printf("THREAD_B: la cerise "); printf("sur le gateaun"); Leave. Critical. Section((LPCRITICAL_SECTION)p Critic. Sec); printf("fin du thread Bn"); return 0; } jc/md/lp-01/06 Synchronisation 18
Résultat de l’exécution jc/md/lp-01/06 Synchronisation 19
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é jc/md/lp-01/06 Synchronisation 20
Mutex : fonctions (1) • Création d’un Mutex HANDLE Create. Mutex( LPSECURITY_ATTRIBUTES lp. Mutex. Attributes, BOOL b. Initial. Owner, LPCTSTR lp. Name ); lp. Mutex. Attributes : NULL pour CE b. Initial. Owner : prise ou non du mutex lp. Name : pointeur sur un nom ou NULL valeur retournée : création ou non du mutex, etc. jc/md/lp-01/06 Synchronisation 21
MUTEX : fonctions (2) • Attente de disponibilité du mutex DWORD Wait. For. Single. Object( HANDLE h. Handle, DWORD dw. Milliseconds); h. Handle : handle du mutex dw. Milliseconds : INFINITE (pas de time-out) valeur de retour : état du mutex • Libération du mutex BOOL Release. Mutex(HANDLE h. Mutex ); valeur de retour : réussite ou non jc/md/lp-01/06 Synchronisation 22
MUTEX : fonctions (2) • Attente de disponibilité du mutex DWORD Wait. For. Single. Object( HANDLE h. Handle, DWORD dw. Milliseconds); h. Handle : handle du mutex dw. Milliseconds : INFINITE (pas de time-out) valeur de retour : état du mutex • Libération du mutex BOOL Release. Mutex(HANDLE h. Mutex ); valeur de retour : réussite ou non jc/md/lp-01/06 Synchronisation 23
Application MUTEX • Créer une application MUTEX • Utiliser les mutexes pour que les textes ne soient plus mélangés lors de l’impression jc/md/lp-01/06 Synchronisation 24
MUTEX main (1) #include "stdafx. h" DWORD WINAPI THREAD_A(HANDLE h. Mutex); DWORD WINAPI THREAD_B(HANDLE h. Mutex); int WINAPI Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { HANDLE h. Mutex, H 1, H 2; printf("debut du main MUTEXn"); h. Mutex=Create. Mutex(NULL, FALSE, NULL); jc/md/lp-01/06 Synchronisation 25
MUTEX main (2) H 1=Create. Thread(0, 0, THREAD_A, h. Mutex, 0, 0); H 2=Create. Thread(0, 0, THREAD_B, (LPVOID)h. Mutex, 0, 0); Sleep(5000); Close. Handle(H 1); Close. Handle(H 2); Close. Handle(h. Mutex); printf("fin du main MUTEXn"); getchar(); return 0; } jc/md/lp-01/06 Synchronisation 26
MUTEX thread_A DWORD WINAPI THREAD_A(HANDLE h. Mut) { printf("debut du thread An"); Wait. For. Single. Object(h. Mut, INFINITE); printf("THREAD_A: le loup et "); printf("l'agneaun"); Release. Mutex(h. Mut); printf("fin du thread An"); return 0; } jc/md/lp-01/06 Synchronisation 27
MUTEX Thread_B DWORD WINAPI THREAD_B(HANDLE h. Mut) { printf("debut du thread Bn"); Wait. For. Single. Object(h. Mut, INFINITE); printf("THREAD_B: la cerise "); printf("sur le gateaun"); Release. Mutex(h. Mut); printf("fin du thread Bn"); return 0; } jc/md/lp-01/06 Synchronisation 28
Résultat de l’exécution jc/md/lp-01/06 Synchronisation 29
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 jc/md/lp-01/06 Synchronisation 30
Événements : fonctions (1) • Création d’un événement HANDLE Create. Event( LPSECURITY_ATTRIBUTES lp. Event. Attributes, BOOL b. Manual. Reset, BOOL b. Initial. State, LPTSTR lp. Name ); lp. Event. Attributes: NULL pour CE b. Manual. Reset: TRUE autorise Reset. Event b. Initial. State: TRUE événement disponible lp. Name: NULL pour un événement sans nom jc/md/lp-01/06 Synchronisation 31
Événements : fonctions (2) • Ouverture d’événement HANDLE Open. Event( DWORD dw. Desired. Acess, BOOL b. Inherit. Handle, LPCTSTR lp. Name); • Fournit un handle sur un événement déjà créé par Create. Event • Correspondance par le nom lp. Name jc/md/lp-01/06 Synchronisation 32
Événements : fonctions (3) • Attente d’événement DWORD Wait. For. Single. Object( HANDLE h. Handle, DWORD dw. Milliseconds); • Activation de l’événement BOOL Set. Event(HANDLE h. Event); • Désactivation de l’événement BOOL Reset. Event(HANDLE h. Event); jc/md/lp-01/06 Synchronisation 33
Événements : fonctions (4) • Dépôt d’une donnée BOOL Set. Event. Data(HANDLE h. Event, DWORD dw. Data); h. Event : handle de l’événement dw. Data : donnée à affecter • Récupération de la donnée déposée DWORD Get. Event. Data(HANDLE h. Event); valeur de retour : la donnée déposée jc/md/lp-01/06 Synchronisation 34
Application EVENT • Créer une application EVENT • Event. A est créé actif dans le thread A pour autoriser la tâche A à démarrer • Event. B est créé inactif dans le thread B • La tâche A désactivera Event. A en début de tâche et activera Event. B en fin de tâche • La tâche B désactivera Event. B en début de tâche et réactivera Event. A en fin de tâche • Les 2 tâches vont s’activer mutuellement jc/md/lp-01/06 Synchronisation 35
EVENT main (1) #include "stdafx. h" DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); HANDLE h. Event. A, h. Event. B; //variables globales pour… int WINAPI Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { HANDLE H 1, H 2; printf("debut du main EVENTn"); jc/md/lp-01/06 Synchronisation 36
EVENT main (2) h. Event. A=Create. Event(NULL, TRUE, NULL); h. Event. B=Create. Event(NULL, TRUE, FALSE, NULL); H 1=Create. Thread(0, 0, THREAD_A, 0, 0, 0); H 2=Create. Thread(0, 0, THREAD_B, 0, 0, 0); Sleep(5000); Close. Handle(H 1); Close. Handle(H 2); Close. Handle(h. Event. A); Close. Handle(h. Event. B); printf("fin du main EVENTn"); getchar(); return 0; } jc/md/lp-01/06 Synchronisation 37
EVENT THREAD_A DWORD WINAPI THREAD_A(LPVOID p) { printf("debut du thread An"); Wait. For. Single. Object(h. Event. A, INFINITE); printf("THREAD_A: le loup et "); printf("l'agneaun"); Reset. Event(h. Event. A); Set. Event(h. Event. B); printf("fin thread An"); return 0; } jc/md/lp-01/06 Synchronisation 38
EVENT THREAD_B DWORD WINAPI THREAD_B(LPVOID p) { printf("debut thread Bn"); Wait. For. Single. Object(h. Event. B, INFINITE); printf("THREAD_B: la cerise "); printf("sur le gateaun"); Reset. Event(h. Event. B); Set. Event(h. Event. A); printf("fin thread Bn"); return 0; } jc/md/lp-01/06 Synchronisation 39
Résultat de l’exécution jc/md/lp-01/06 Synchronisation 40
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 jc/md/lp-01/06 Synchronisation 41
EVENT_NOM : main (1) // EVENT_NOM. cpp : Defines the entry point for the… #include "stdafx. h" #include "Pkfuncs. h"//pour les fonctions Set. Event. Data… DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); int WINAPI Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) jc/md/lp-01/06 Synchronisation 42
EVENT_NOM : main (2) { HANDLE H 1, H 2; HANDLE h. Event. A, h. Event. B; LPTSTR p. Nom. Event. A={L"NOM_EVENT_A"}; LPTSTR p. Nom. Event. B={L"NOM_EVENT_B"}; printf("debut du main EVENT_NOMn"); h. Event. A=Create. Event(NULL, TRUE, p. Nom. Event. A); Set. Event. Data(h. Event. A, 1); h. Event. B=Create. Event(NULL, TRUE, FALSE, p. Nom. Event. B); H 1=Create. Thread(0, 0, THREAD_A, 0, 0, 0); H 2=Create. Thread(0, 0, THREAD_B, 0, 0, 0); jc/md/lp-01/06 Synchronisation 43
EVENT_NOM : main (3) Sleep(5000); Close. Handle(H 1); Close. Handle(H 2); Close. Handle(h. Event. A); Close. Handle(h. Event. B); printf("fin du main EVENT_NOMn"); getchar(); return 0; } jc/md/lp-01/06 Synchronisation 44
EVENT_NOM : THREAD_A (1) DWORD WINAPI THREAD_A(LPVOID p) { DWORD dw. Data_A; DWORD dw. Time_out=1000; HANDLE h. Event_A, h. Event_B; LPTSTR p. Nom. Event. A={L"NOM_EVENT_A"}; LPTSTR p. Nom. Event. B={L"NOM_EVENT_B"}; printf("debut du thread An"); h. Event_A=Open. Event(EVENT_ALL_ACCESS, FALSE, p. Nom. Event. A); h. Event_B=Open. Event(EVENT_ALL_ACCESS, FALSE, p. Nom. Event. B); jc/md/lp-01/06 Synchronisation 45
EVENT_NOM : THREAD_A (2) while(WAIT_OBJECT_0==Wait. For. Single. Object(h. Event_A, dw. Time_out)) { dw. Data_A=Get. Event. Data(h. Event_A); printf("THREAD_A: fable %d : ", dw. Data_A); switch (dw. Data_A) { case 1 : printf("le loup "); printf("et l'agneaun"); break; case 2 : printf("le lion et "); printf("le ratn"); break; default : break; } jc/md/lp-01/06 Synchronisation 46
EVENT_NOM : THREAD_A (3) Reset. Event(h. Event_A); Set. Event(h. Event_B); } printf("fin thread An"); return 0; jc/md/lp-01/06 Synchronisation 47
EVENT_NOM : THREAD_B (1) DWORD WINAPI THREAD_B(LPVOID p) { HANDLE h. Event_A, h. Event_B; LPTSTR p. Nom. Event. A={L"NOM_EVENT_A"}; LPTSTR p. Nom. Event. B={L"NOM_EVENT_B"}; printf("debut thread Bn"); h. Event_A=Open. Event(EVENT_ALL_ACCESS, FALSE, p Nom. Event. A); Set. Event. Data(h. Event_A, 2); h. Event_B=Open. Event(EVENT_ALL_ACCESS, FALSE, p Nom. Event. B); Wait. For. Single. Object(h. Event_B, INFINITE); jc/md/lp-01/06 Synchronisation 48
EVENT_NOM : THREAD_B (2) printf("THREAD_B: la cerise "); printf("sur le gateaun"); Reset. Event(h. Event_B); Set. Event(h. Event_A); Sleep(2000); //suivant valeur : fin de A avant B ou de B avant A printf("fin thread Bn"); return 0; } jc/md/lp-01/06 Synchronisation 49
Résultat de l’exécution jc/md/lp-01/06 Synchronisation 50
Sémaphore (1) • Contrôle le nombre des accès à une ressource par la distribution de jetons • Valeur maximale fixée à la création • Chaque utilisateur prend et restitue un ou plusieurs jetons sur le sémaphore • Fonctionne entre processus indépendants • Exclusion mutuelle dans le seul cas d’un jeton à valeur maximum de 1 jc/md/lp-01/06 Synchronisation 51
Sémaphore (2) • Le nombre de jetons disponibles est égal à tout instant au nombre des utilisateurs de la ressource gérée par le sémaphore • Chaque fois qu’un un jeton est pris, le compteur de jeton est décrémenté • Chaque fois qu’un jeton est restitué, le compteur de jeton est incrémenté • Lorsque le nombre de jetons disponibles est 0, la ressource n’est plus disponible jc/md/lp-01/06 Synchronisation 52
Sémaphores : fonctions (1) • Création d’un sémaphore sans nom ou nommé Create. Semaphore • Prise de jeton Wait. For. Single. Object • Restitution de jeton Release. Semaphore • Fermeture : Close. Handle déjà rencontrée • Ouverture d’un sémaphore nommé : la fonction n’est pas implémentée mais Create. Semaphore peut la remplacer jc/md/lp-01/06 Synchronisation 53
Sémaphores : fonctions (2) HANDLE Create. Semaphore( LPSECURITY_ATTRIBUTES lp. Semaphore. Attributes, LONG l. Initial. Count, LONG l. Maximum. Count, LPCTSTR lp. Name ); • Arguments – – • p. Semaphore. Attributes: inutilisé, NULL pour Windows CE l. Initial. Count: jetons mis en jeu à la création l. Maximum. Count: valeur maximale du compteur de jetons lp. Name: NULL pour un sémaphore sans nom ou pointeur sur un nom pour un sémaphore nommé Valeur de retour : NULL si échec ou handle jc/md/lp-01/06 Synchronisation 54
Sémaphores : fonctions (3) • Wait. For. Single. Object : déjà rencontrée • BOOL Release. Semaphore( HANDLE h. Semaphore, LONG l. Release. Count, LPLONG); – Arguments h. Semaphore: handle fourni à la création l. Release. Count: nombre des jetons à restituer lp. Previous. Count: pointeur sur une variable qui sera garnie par le compteur de jeton avant la mise à jour – Valeur de retour : réussite ou non jc/md/lp-01/06 Synchronisation 55
Application SEMA • Créer une application SEMA • Ressource contrôlée : impression d’un message • Utiliser un sémaphore à valeur maximum de 2 pour simuler une ressource qu’on ne peut attribuer que deux fois jc/md/lp-01/06 Synchronisation 56
SEMA main (1) #include "stdafx. h" DWORD WINAPI THREAD_A(LPVOID p); DWORD WINAPI THREAD_B(LPVOID p); HANDLE h. Sem; int WINAPI Win. Main(HINSTANCE h. Instance, HINSTANCE h. Prev. Instance, LPTSTR lp. Cmd. Line, int n. Cmd. Show) { HANDLE H 1, H 2; printf("debut du main SEMAn"); jc/md/lp-01/06 Synchronisation 57
SEMA main (2) h. Sem=Create. Semaphore(NULL, 2, 2, NULL); H 1=Create. Thread(0, 0, THREAD_A, 0, 0, 0); H 2=Create. Thread(0, 0, THREAD_B, 0, 0, 0); Sleep(5000); Close. Handle(H 1); Close. Handle(H 2); Close. Handle(h. Sem); printf("fin du main SEMAn"); getchar(); return 0; } jc/md/lp-01/06 Synchronisation 58
SEMA THREAD_A DWORD WINAPI THREAD_A(LPVOID p) { printf("debut du thread An"); Wait. For. Single. Object(h. Sem, INFINITE); printf("THREAD_A: le loup et l'agneaun"); Wait. For. Single. Object(h. Sem, INFINITE); printf("THREAD_A: le lion et le ratn"); Release. Semaphore(h. Sem, 2, NULL); printf("fin du thread An"); return 0; } jc/md/lp-01/06 Synchronisation 59
SEMA THREAD_B DWORD WINAPI THREAD_B(LPVOID p) { printf("debut du thread Bn"); Sleep(1000); //essayer en commentant la ligne Wait. For. Single. Object(h. Sem, INFINITE); printf("THREAD_B: la cerise sur le gateaun"); Release. Semaphore(h. Sem, 1, NULL); printf("fin du thread Bn"); return 0; } jc/md/lp-01/06 Synchronisation 60
Résultat de l’exécution avec délai jc/md/lp-01/06 Synchronisation 61
Résultat de l’exécution sans délai jc/md/lp-01/06 Synchronisation 62
Résultat de l’exécution perturbée jc/md/lp-01/06 Synchronisation 63
Événements multiples Windows CE offre la possibilité de gérer plusieurs événements par la fonction Wait. For. Multiple. Objects. Les principes sont similaires mais on utilise des événements enregistrés grâce à un tableau. Le premier élément activé rencontré joue le même rôle qu’un événement unique avec Wait. For. Single. Object. jc/md/lp-01/06 Synchronisation 64
Conclusion • Les différentes méthodes de synchronisation ont été appliquées sur des exemples concrets jc/md/lp-01/06 Synchronisation 65
- Slides: 65