jc/md/lp-01/06 Synchronisation 1
Synchronisation
jc/md/lp-01/06 Synchronisation 2
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 3
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 4
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 5
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;
jc/md/lp-01/06 Synchronisation 6
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;
}
jc/md/lp-01/06 Synchronisation 7
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;}
jc/md/lp-01/06 Synchronisation 8
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;}
jc/md/lp-01/06 Synchronisation 9
Résultat de l’exécution
jc/md/lp-01/06 Synchronisation 10
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 11
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é
jc/md/lp-01/06 Synchronisation 12
Section critique : fonctions (1)
• Initialisation d’une section critique void InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection );
• Entrée en section critiquevoid EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection );
• Sortie d’une section critiquevoid LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection );
jc/md/lp-01/06 Synchronisation 13
Section critique : fonctions (2)
• Restitution des ressources
void DeleteCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
• Variante d’entrée non bloquanteBOOL TryEnterCriticalSection( LPCRITICAL_SECTION lpCriticalSection );
jc/md/lp-01/06 Synchronisation 14
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 15
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;
jc/md/lp-01/06 Synchronisation 16
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;
}
jc/md/lp-01/06 Synchronisation 17
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;}
jc/md/lp-01/06 Synchronisation 18
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;}
jc/md/lp-01/06 Synchronisation 19
Résultat de l’exécution
jc/md/lp-01/06 Synchronisation 20
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 21
Mutex : fonctions (1)
• Création d’un MutexHANDLE 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.
jc/md/lp-01/06 Synchronisation 22
MUTEX : fonctions (2)
• Attente de disponibilité du mutexDWORD WaitForSingleObject(
HANDLE hHandle,DWORD dwMilliseconds);
hHandle : handle du mutexdwMilliseconds : INFINITE (pas de time-out)valeur de retour : état du mutex
• Libération du mutexBOOL ReleaseMutex(HANDLE hMutex );valeur de retour : réussite ou non
jc/md/lp-01/06 Synchronisation 23
MUTEX : fonctions (2)
• Attente de disponibilité du mutexDWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds); hHandle : handle du mutexdwMilliseconds : INFINITE (pas de time-out)valeur de retour : état du mutex
• Libération du mutexBOOL ReleaseMutex(HANDLE hMutex );valeur de retour : réussite ou non
jc/md/lp-01/06 Synchronisation 24
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 25
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);
jc/md/lp-01/06 Synchronisation 26
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;
}
jc/md/lp-01/06 Synchronisation 27
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;
}
jc/md/lp-01/06 Synchronisation 28
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;
}
jc/md/lp-01/06 Synchronisation 29
Résultat de l’exécution
jc/md/lp-01/06 Synchronisation 30
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 31
É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
jc/md/lp-01/06 Synchronisation 32
É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
jc/md/lp-01/06 Synchronisation 33
É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énementBOOL ResetEvent(HANDLE hEvent);
jc/md/lp-01/06 Synchronisation 34
É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
jc/md/lp-01/06 Synchronisation 35
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
jc/md/lp-01/06 Synchronisation 36
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");
jc/md/lp-01/06 Synchronisation 37
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;
}
jc/md/lp-01/06 Synchronisation 38
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;
}
jc/md/lp-01/06 Synchronisation 39
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;
}
jc/md/lp-01/06 Synchronisation 40
Résultat de l’exécution
jc/md/lp-01/06 Synchronisation 41
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 42
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)
jc/md/lp-01/06 Synchronisation 43
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);
jc/md/lp-01/06 Synchronisation 44
EVENT_NOM : main (3)
Sleep(5000);
CloseHandle(H1);
CloseHandle(H2);
CloseHandle(hEventA);
CloseHandle(hEventB);
printf("fin du main EVENT_NOM\n");
getchar();
return 0;
}
jc/md/lp-01/06 Synchronisation 45
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);
jc/md/lp-01/06 Synchronisation 46
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; }
jc/md/lp-01/06 Synchronisation 47
EVENT_NOM : THREAD_A (3)
ResetEvent(hEvent_A);
SetEvent(hEvent_B);
}
printf("fin thread A\n");
return 0;
jc/md/lp-01/06 Synchronisation 48
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);
jc/md/lp-01/06 Synchronisation 49
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;
}
jc/md/lp-01/06 Synchronisation 50
Résultat de l’exécution
jc/md/lp-01/06 Synchronisation 51
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 52
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 53
Sémaphores : fonctions (1)
• Création d’un sémaphore sans nom ou nomméCreateSemaphore
• Prise de jetonWaitForSingleObject
• Restitution de jetonReleaseSemaphore
• Fermeture : CloseHandle déjà rencontrée • Ouverture d’un sémaphore nommé : la fonction
n’est pas implémentée mais CreateSemaphore peut la remplacer
jc/md/lp-01/06 Synchronisation 54
Sémaphores : fonctions (2)
HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName );
• Arguments– pSemaphoreAttributes: inutilisé, NULL pour Windows CE– lInitialCount: jetons mis en jeu à la création– lMaximumCount: valeur maximale du compteur de jetons– lpName: 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 55
Sémaphores : fonctions (3)
• WaitForSingleObject : déjà rencontrée• BOOL ReleaseSemaphore(
HANDLE hSemaphore,LONG lReleaseCount,LPLONG);
– ArgumentshSemaphore: handle fourni à la créationlReleaseCount: nombre des jetons à restituerlpPreviousCount: 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 56
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 57
SEMA main (1)
#include "stdafx.h"DWORD WINAPI THREAD_A(LPVOID p);DWORD WINAPI THREAD_B(LPVOID p); HANDLE hSem;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){
HANDLE H1,H2;printf("debut du main SEMA\n");
jc/md/lp-01/06 Synchronisation 58
SEMA main (2)
hSem=CreateSemaphore(NULL,2,2,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(hSem); printf("fin du main SEMA\n"); getchar();
return 0;}
jc/md/lp-01/06 Synchronisation 59
SEMA THREAD_A
DWORD WINAPI THREAD_A(LPVOID p){
printf("debut du thread A\n"); WaitForSingleObject(hSem,INFINITE); printf("THREAD_A: le loup et l'agneau\n"); WaitForSingleObject(hSem,INFINITE); printf("THREAD_A: le lion et le rat\n"); ReleaseSemaphore(hSem,2,NULL); printf("fin du thread A\n"); return 0;
}
jc/md/lp-01/06 Synchronisation 60
SEMA THREAD_B
DWORD WINAPI THREAD_B(LPVOID p){
printf("debut du thread B\n"); Sleep(1000); //essayer en commentant la ligne WaitForSingleObject(hSem,INFINITE); printf("THREAD_B: la cerise sur le gateau\n"); ReleaseSemaphore(hSem,1,NULL); printf("fin du thread B\n"); return 0;
}
jc/md/lp-01/06 Synchronisation 61
Résultat de l’exécution avec délai
jc/md/lp-01/06 Synchronisation 62
Résultat de l’exécution sans délai
jc/md/lp-01/06 Synchronisation 63
Résultat de l’exécution perturbée
jc/md/lp-01/06 Synchronisation 64
Événements multiples
Windows CE offre la possibilité de gérer plusieurs événements par la fonction WaitForMultipleObjects.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 WaitForSingleObject.
jc/md/lp-01/06 Synchronisation 65
Conclusion
• Les différentes méthodes de synchronisation ont été appliquées sur des exemples concrets
Top Related