A priori, quand on lance des threads, on s'attend à ce qu'ils agissent sans qu'on puisse déterminer dans quel ordre ils vont le faire.
Dans certains cas, nous voulons au contraire qu'ils agissent dans un ordre bien déterminé.
Voilà un exemple de solution possible.
Le principe est simple:
Pour l'exemple:
On voit bien dans l'impression de sortie que chaque thread agit bien à son tour, dans l'ordre des valeurs successives de “statut”.
#!/usr/bin/python # -*- coding: utf-8 -*- import threading import time ############################################################################### class Acteur(threading.Thread): def __init__(self, nom, st1, st2): threading.Thread.__init__(self) self.setName(nom) self.st1 = st1 # = statut qui déclenche l'action self.st2 = st2 # = statut pour passer la main au thread suivant self.marche = True # drapeau pour stopper le thread à la demande def run(self): global statut, vstatut, vprint while self.marche: # attente du bon statut avant l'action prévue: while True: vstatut.acquire() if statut==self.st1 or not self.marche: vstatut.release() break vstatut.release() time.sleep(0.1) # action prévue par le thread if self.marche: vprint.acquire() print "action faite par " + self.getName() vprint.release() # fin de l'action: le thread passe la main au suivant vstatut.acquire() statut=self.st2 vstatut.release() def stop(self): self.marche = False ############################################################################### print "debut" # création de la variable globale partagée statut et de son verrou statut = 0 vstatut = threading.Lock() # création d'un verrou pour l'impression par les thread vprint = threading.Lock() # creation d'une liste de thread nbacteurs = 10 acteurs = [] for i in xrange(0,nbacteurs): if i==(nbacteurs-1): k = 0 else: k = i+1 a = Acteur("A%d" % i, i, k) a.setDaemon(True) acteurs.append(a) # lancement de tous les threads for i in xrange(0,nbacteurs): acteurs[i].start() # attente 5 secondes time.sleep(5) # arrêt de tous les threads for i in xrange(0,nbacteurs): acteurs[i].stop() # attente pour terminer que tous les threads soient arrêtés for i in xrange(0,nbacteurs): acteurs[i].join() vprint.acquire() print "fin du thread " + acteurs[i].getName() vprint.release() print "fini"
Ce programme affiche:
debut action faite par A0 action faite par A1 action faite par A2 action faite par A3 action faite par A4 action faite par A5 action faite par A6 action faite par A7 action faite par A8 action faite par A9 action faite par A0 action faite par A1 action faite par A2 action faite par A3 action faite par A4 ... ... ... action faite par A5 action faite par A6 action faite par A7 action faite par A8 action faite par A9 action faite par A0 action faite par A1 action faite par A2 action faite par A3 stopper le thread A0 stopper le thread A1 stopper le thread A2 stopper le thread A3 stopper le thread A4 stopper le thread A5 stopper le thread A6 stopper le thread A7 stopper le thread A8 stopper le thread A9 fini
C'est la même chose qu'un verrou, et cela affiche la même chose, à part qu'on peut espérer que l'attente de la condition (.wait()) consomme moins de ressources machine que la boucle while d'attente du bon statut.
#!/usr/bin/python # -*- coding: utf-8 -*- import threading import time ############################################################################### class Acteur(threading.Thread): def __init__(self, nom, st1, st2): threading.Thread.__init__(self) self.setName(nom) self.st1 = st1 # = statut qui déclenche l'action self.st2 = st2 # = statut pour passer la main au thread suivant self.marche = True # drapeau pour stopper le thread à la demande def run(self): global statut, cstatut, vprint while self.marche: # attente du bon statut avant l'action prévue: cstatut.acquire() if statut!=self.st1: cstatut.wait() cstatut.release() time.sleep(0.1) # action prévue par le thread if self.marche: vprint.acquire() print "action faite par " + self.getName() vprint.release() # fin de l'action: le thread passe la main au suivant cstatut.acquire() #print self.getName(), statut statut=self.st2 cstatut.notifyAll() cstatut.release() def stop(self): self.marche = False ############################################################################### print "debut" # création de la variable globale partagée statut et de son verrou statut = 0 cstatut = threading.Condition() # création d'un verrou pour l'impression par les thread vprint = threading.Lock() # creation d'une liste de thread nbacteurs = 10 acteurs = [] for i in xrange(0,nbacteurs): if i==(nbacteurs-1): k = 0 else: k = i+1 a = Acteur("A%d" % i, i, k) a.setDaemon(True) acteurs.append(a) # lancement de tous les threads for i in xrange(0,nbacteurs): acteurs[i].start() # attente 5 secondes time.sleep(5) # arrêt de tous les threads for i in xrange(0,nbacteurs): acteurs[i].stop() # attente pour terminer que tous les threads soient arrêtés for i in xrange(0,nbacteurs): acteurs[i].join() vprint.acquire() print "fin du thread " + acteurs[i].getName() vprint.release() print "fini"