Maîtriser la programmation multithread en Python : Techniques de concurrence efficaces
Exécuter plusieurs threads revient à exécuter plusieurs programmes simultanément, mais avec les avantages suivants :
Plusieurs threads au sein d'un processus partagent le même espace de données que le thread principal et peuvent donc partager des informations ou communiquer plus facilement que s'ils étaient des processus séparés.
Les threads, parfois appelés processus légers, ne nécessitent pas beaucoup de surcharge mémoire ; ils sont moins coûteux que les processus.
Un thread a un début, une séquence d'exécution et une conclusion. Il a un pointeur d'instruction qui suit où il s'exécute dans son contexte.
Il peut être préempté (interrompu).
Il peut être mis en attente temporaire (sommeil) pendant que d'autres threads s'exécutent : c'est le yielding.
Démarrage d'un nouveau thread
Pour engendrer un autre thread, appelez la méthode suivante du module thread :
thread.start_new_thread(function, args[, kwargs])
Cet appel permet une création rapide et efficace de threads sous Linux et Windows.
L'appel retourne immédiatement et le thread enfant démarre et appelle function avec la liste args. Quand la fonction retourne, le thread se termine.
args est un tuple d'arguments ; utilisez un tuple vide pour appeler sans arguments. kwargs est un dictionnaire optionnel d'arguments nommés.
Exemple
#!/usr/bin/python
import thread
import time
def print_time(threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print("%s: %s" % (threadName, time.ctime(time.time())))
try:
thread.start_new_thread(print_time, ("Thread-1", 2))
thread.start_new_thread(print_time, ("Thread-2", 4))
except:
print("Error: unable to start thread")
while 1:
pass
Résultat :
Thread-1: Thu Jan 22 15:42:17 2009
Thread-1: Thu Jan 22 15:42:19 2009
Thread-2: Thu Jan 22 15:42:19 2009
Thread-1: Thu Jan 22 15:42:21 2009
Thread-2: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:23 2009
Thread-1: Thu Jan 22 15:42:25 2009
Thread-2: Thu Jan 22 15:42:27 2009
Thread-2: Thu Jan 22 15:42:31 2009
Thread-2: Thu Jan 22 15:42:35 2009
Bien qu'efficace pour le threading de bas niveau, le module thread est très limité comparé au module threading plus récent.
Le module Threading
Le module threading inclus avec Python 2.4 fournit un support bien plus puissant et de haut niveau pour les threads que le module thread.
Le module threading expose toutes les méthodes du module thread et fournit des méthodes supplémentaires :
threading.activeCount() : Nombre d'objets thread actifs.
threading.currentThread() : Objet thread de l'appelant.
threading.enumerate() : Liste de tous les objets thread actuellement actifs.
En plus, le module threading a la classe Thread qui implémente le threading. Méthodes de la classe Thread :
run() : Point d'entrée d'un thread.
start() : Démarre un thread en appelant
run().join([time]) : Attend la terminaison des threads.
isAlive() : Vérifie si un thread s'exécute encore.
getName() : Renvoie le nom d'un thread.
setName() : Définit le nom d'un thread.
Création d'un thread avec le module Threading
Pour implémenter un nouveau thread avec threading :
Définissez une nouvelle sous-classe de la classe
Thread.Redéfinissez
__init__(self[, args])pour ajouter des arguments.Redéfinissez
run(self[, args])pour implémenter ce que le thread doit faire au démarrage.
Une fois la sous-classe créée, instanciez-la et démarrez un nouveau thread avec start(), qui appelle run().
Exemple
#!/usr/bin/python
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
print_time(self.name, 5, self.counter)
print("Exiting " + self.name)
def print_time(threadName, counter, delay):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# Créer nouveaux threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Démarrer nouveaux threads
thread1.start()
thread2.start()
print("Exiting Main Thread")
Résultat :
Starting Thread-1
Starting Thread-2
Exiting Main Thread
Thread-1: Thu Mar 21 09:10:03 2013
Thread-1: Thu Mar 21 09:10:04 2013
Thread-2: Thu Mar 21 09:10:04 2013
Thread-1: Thu Mar 21 09:10:05 2013
Thread-1: Thu Mar 21 09:10:06 2013
Thread-2: Thu Mar 21 09:10:06 2013
Thread-1: Thu Mar 21 09:10:07 2013
Exiting Thread-1
Thread-2: Thu Mar 21 09:10:08 2013
Thread-2: Thu Mar 21 09:10:10 2013
Thread-2: Thu Mar 21 09:10:12 2013
Exiting Thread-2
Synchronisation des threads
Le module threading inclut un mécanisme de verrouillage simple pour synchroniser les threads. Un nouveau verrou est créé par Lock().
La méthode acquire(blocking) force les threads à s'exécuter de façon synchrone. Le paramètre optionnel blocking contrôle si le thread attend pour acquérir le verrou.
Si
blocking= 0 : retour immédiat avec 0 si verrou non acquis, 1 si acquis.Si
blocking= 1 : le thread bloque et attend la libération du verrou.
La méthode release() libère le verrou quand il n'est plus nécessaire.
Exemple
#!/usr/bin/python
import threading
import time
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
threadLock.acquire()
print_time(self.name, self.counter, 3)
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
threads.append(thread1)
threads.append(thread2)
for t in threads:
t.join()
print("Exiting Main Thread")
Résultat :
Starting Thread-1
Starting Thread-2
Thread-1: Thu Mar 21 09:11:28 2013
Thread-1: Thu Mar 21 09:11:29 2013
Thread-1: Thu Mar 21 09:11:30 2013
Thread-2: Thu Mar 21 09:11:32 2013
Thread-2: Thu Mar 21 09:11:34 2013
Thread-2: Thu Mar 21 09:11:36 2013
Exiting Main Thread
File de priorité multithread
Le module Queue permet de créer un objet file pouvant contenir un nombre spécifique d'éléments. Méthodes de contrôle :
get() : Retire et renvoie un élément de la file.
put() : Ajoute un élément à la file.
qsize() : Nombre d'éléments actuellement dans la file.
empty() :
Truesi file vide, sinonFalse.full() :
Truesi file pleine, sinonFalse.
Exemple
#!/usr/bin/python
import Queue
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print("Starting " + self.name)
process_data(self.name, self.q)
print("Exiting " + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
while not workQueue.empty():
pass
exitFlag = 1
for t in threads:
t.join()
print("Exiting Main Thread")
Résultat :
Starting Thread-1
Starting Thread-2
Starting Thread-3
Thread-1 processing One
Thread-2 processing Two
Thread-3 processing Three
Thread-1 processing Four
Thread-2 processing Five
Exiting Thread-3
Exiting Thread-1
Exiting Thread-2
Exiting Main Thread
Python
- Maîtriser les types de données Python : Guide pratique
- Maîtriser les opérateurs Python : Guide complet
- Maîtriser l'instruction pass en Python : Guide pratique
- Maîtriser les arguments de fonctions Python : Positionnels, nommés et paramètres par défaut
- Maîtriser les dictionnaires Python : création, manipulation et techniques avancées
- Maîtriser la programmation orientée objet Python : classes, héritage, encapsulation et polymorphisme
- Programmation réseau en Python : Maîtriser les sockets et protocoles avancés
- Maîtriser la programmation d'extensions Python avec C : Construire des modules natifs rapides
- Maîtriser le Cycle de Filetage Fanuc G76 : Guide Complet pour Opérateurs CNC