• Aucun résultat trouvé

donnéesPilote de base de données

5. On peut aussi attendre la fin de son exécution par T.join() On a là une instruction bloquante : le programme qui l'exécute est bloqué jusqu'à ce que la tâche T ait terminé son travail C'est un moyen de synchronisation.

7.5 Accès exclusif à une ressource partagée

Dans notre exemple, la section critique est le code situé entre la lecture du compteur et l'écriture de sa nouvelle valeur :

' lecture compteur

Dim valeur As Integer = cptrThreads ' attente

Thread.Sleep(1000)

' incrémentation compteur cptrThreads = valeur + 1

Les threads d'exécution 181 Pour exécuter ce code, un thread doit être assuré d'être tout seul. Il peut être interrompu mais pendant cette interruption, un autre thread ne doit pas pouvoir exécuter ce même code. La plate-forme .NET offre plusieurs outils pour assurer l'entrée unitaire dans les sections critiques de code. Nous utiliserons la classe Mutex :

Nous n'utiliserons ici que les constructeurs et méthodes suivants :

public Mutex() crée un objet de synchronisation M

public bool WaitOne() Le thread T1 qui exécute l'opération M.WaitOne() demande la propriété de l'objet de

synchronisation M. Si le Mutex M n'est détenu par aucun thread (le cas au départ), il est "donné" au thread T1 qui l'a demandé. Si un peu plus tard, un thread T2 fait la même opération, il sera bloqué. En effet, un Mutex ne peut appartenir qu'à un thread. Il sera débloqué lorsque le thread T1 libèrera le mutex M qu'il détient. Plusieurs threads peuvent ainsi être bloqués en attente du Mutex M.

public void

ReleaseMutex() Le thread T1 qui effectue l'opération M.ReleaseMutex() abandonne la propriété du Mutex M.Lorsque le thread T1 perdra le processeur, le système pourra le donner à l'un des threads en attente du

Mutex M. Un seul l'obtiendra à son tour, les autres en attente de M restant bloqués

Un Mutex M gère l'accès à une ressource partagée R. Un thread demande la ressource R par M.WaitOne() et la rend par M.ReleaseMutex(). Une section critique de code qui ne doit être exécutée que par un seul thread à la fois est une ressource partagée. La synchronisation d'exécution de la section critique peut se faire ainsi :

M.WaitOne()

' le thread est seul à entrer ici ' section critique

....

M.ReleaseMutex()

où M est un objet Mutex. Il faut bien sûr ne jamais oublier de libérer un Mutex devenu inutile pour qu'un autre thread puisse entrer dans la section critique, sinon les threads en attente d'un Mutex jamais libéré n'auront jamais accès au processeur. Par ailleurs, il faut éviter la situation d'interblocage (deadlock) dans laquelle deux threads s'attendent mutuellement. Considérons les actions suivantes qui se suivent dans le temps :

§ un thread T1 obtient la propriété d'un Mutex M1 pour avoir accès à une ressource partagée R1 § un thread T2 obtient la propriété d'un Mutex M2 pour avoir accès à une ressource partagée R2 § le thread T1 demande le Mutex M2. Il est bloqué.

§ le thread T2 demande le Mutex M1. Il est bloqué.

Ici, les threads T1 et T2 s'attendent mutuellement. Ce cas apparaît lorsque des threads ont besoin de deux ressources partagées, la ressource R1 contrôlée par le Mutex M1 et la ressource R2 contrôlée par le Mutex M2. Une solution possible est de demander les deux ressources en même temps à l'aide d'un Mutex unique M. Mais ce n'est pas toujours possible si par exemple cela entraîne une mobilisation longue d'une ressource coûteuse. Une autre solution est qu'un thread ayant M1 et ne pouvant obtenir M2, relâche alors M1 pour éviter l'interblocage. Si nous mettons en pratique ce que nous venons de voir sur l'exemple précédent, notre application devient la suivante : ' options Option Explicit On Option Strict On ' utilisation de threads Imports System Imports System.Threading

Public Class thread4

' variables de classe

Les threads d'exécution 182 Private Shared autorisation As Mutex

Public Overloads Shared Sub Main(ByVal args() As [String]) ' mode d'emploi

Const syntaxe As String = "pg nbThreads" Const nbMaxThreads As Integer = 100 ' vérification nbre d'arguments

If args.Length <> 1 Then ' erreur Console.Error.WriteLine(syntaxe) ' arrêt Environment.Exit(1) End If

' vérification qualité de l'argument

Dim nbThreads As Integer = 0 Try

nbThreads = Integer.Parse(args(0))

If nbThreads < 1 Or nbThreads > nbMaxThreads Then

Throw New Exception End If

Catch

End Try

' initialisation de l'autorisation d'accès à une section critique

autorisation = New Mutex

' création et génération des threads

Dim threads(nbThreads) As Thread Dim i As Integer

For i = 0 To nbThreads - 1 ' création

threads(i) = New Thread(New ThreadStart(AddressOf incrémente)) ' nommage

threads(i).Name = "tache_" & i ' lancement

threads(i).Start() Next i

' attente de la fin des threads

For i = 0 To nbThreads - 1 threads(i).Join()

Next i

' affichage compteur

Console.Out.WriteLine(("Nombre de threads générés : " & cptrThreads))

End Sub

Public Shared Sub incrémente() ' augmente le compteur de threads

' on demande l'autorisation d'entrer dans la secton critique

autorisation.WaitOne() ' lecture compteur

Dim valeur As Integer = cptrThreads ' suivi

Console.Out.WriteLine(("A " & DateTime.Now.ToString("hh:mm:ss") & ", le thread " & Thread.CurrentThread.Name & " a lu la valeur du compteur : " & cptrThreads))

' attente

Thread.Sleep(1000)

' incrémentation compteur

cptrThreads = valeur + 1 ' suivi

Console.Out.WriteLine(("A " & DateTime.Now.ToString("hh:mm:ss") & ", le thread " & Thread.CurrentThread.Name & " a écrit la valeur du compteur : " & cptrThreads)) ' on rend l'autorisation d'accès

autorisation.ReleaseMutex()

End Sub End Class

Les résultats obtenus sont conformes à ce qui était attendu :

dos>thread4 5

A 05:51:10, le thread tache_0 a lu la valeur du compteur : 0 A 05:51:11, le thread tache_0 a écrit la valeur du compteur : 1 A 05:51:11, le thread tache_1 a lu la valeur du compteur : 1 A 05:51:12, le thread tache_1 a écrit la valeur du compteur : 2 A 05:51:12, le thread tache_2 a lu la valeur du compteur : 2 A 05:51:13, le thread tache_2 a écrit la valeur du compteur : 3 A 05:51:13, le thread tache_3 a lu la valeur du compteur : 3 A 05:51:14, le thread tache_3 a écrit la valeur du compteur : 4 A 05:51:14, le thread tache_4 a lu la valeur du compteur : 4 A 05:51:15, le thread tache_4 a écrit la valeur du compteur : 5 Nombre de threads générés : 5

Les threads d'exécution 183