Python 3 - Programmation multithread
L'exécution de plusieurs threads est similaire à l'exécution simultanée de plusieurs programmes différents, mais avec les avantages suivants:
Plusieurs threads au sein d'un processus partagent le même espace de données avec le thread principal et peuvent donc partager des informations ou communiquer entre eux plus facilement que s'il s'agissait de processus séparés.
Les threads sont parfois appelés processus légers et ne nécessitent pas beaucoup de mémoire; ils sont moins chers que les procédés.
Un thread a un début, une séquence d'exécution et une conclusion. Il a un pointeur d'instruction qui garde une trace de où dans son contexte il est en cours d'exécution.
Il peut être préempté (interrompu).
Il peut être temporairement mis en attente (également connu sous le nom de veille) pendant que d'autres threads sont en cours d'exécution - c'est ce qu'on appelle le rendement.
Il existe deux types de threads différents -
- fil du noyau
- fil utilisateur
Les threads du noyau font partie du système d'exploitation, tandis que les threads de l'espace utilisateur ne sont pas implémentés dans le noyau.
Il existe deux modules qui prennent en charge l'utilisation des threads dans Python3 -
- _thread
- threading
Le module de thread est «obsolète» depuis assez longtemps. Les utilisateurs sont encouragés à utiliser le module de thread à la place. Par conséquent, dans Python 3, le module "thread" n'est plus disponible. Cependant, il a été renommé en "_thread" pour des compatibilités vers l'arrière en Python3.
Démarrer un nouveau fil
Pour générer un autre thread, vous devez appeler la méthode suivante disponible dans le module thread -
_thread.start_new_thread ( function, args[, kwargs] )
Cet appel de méthode permet un moyen rapide et efficace de créer de nouveaux threads à la fois sous Linux et Windows.
L'appel de méthode retourne immédiatement et le thread enfant démarre et appelle la fonction avec la liste d' arguments passée . Lorsque la fonction retourne, le thread se termine.
Ici, args est un tuple d'arguments; utilisez un tuple vide pour appeler la fonction sans passer d'arguments. kwargs est un dictionnaire facultatif d'arguments de mots clés.
Exemple
#!/usr/bin/python3
import _thread
import time
# Define a function for the thread
def print_time( threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print ("%s: %s" % ( threadName, time.ctime(time.time()) ))
# Create two threads as follows
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
Production
Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -
Thread-1: Fri Feb 19 09:41:39 2016
Thread-2: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:41 2016
Thread-1: Fri Feb 19 09:41:43 2016
Thread-2: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:45 2016
Thread-1: Fri Feb 19 09:41:47 2016
Thread-2: Fri Feb 19 09:41:49 2016
Thread-2: Fri Feb 19 09:41:53 2016
Le programme se déroule dans une boucle infinie. Vous devrez appuyer sur ctrl-c pour arrêter
Bien qu'il soit très efficace pour le threading de bas niveau, le module thread est très limité par rapport au module threading plus récent.
Le module de filetage
Le nouveau module de threading inclus avec Python 2.4 fournit une prise en charge beaucoup plus puissante et de haut niveau pour les threads que le module de thread discuté dans la section précédente.
Le module de threading expose toutes les méthodes du module de thread et fournit des méthodes supplémentaires -
threading.activeCount() - Renvoie le nombre d'objets thread qui sont actifs.
threading.currentThread() - Renvoie le nombre d'objets de thread dans le contrôle de thread de l'appelant.
threading.enumerate() - Renvoie une liste de tous les objets thread actuellement actifs.
En plus des méthodes, le module de threading a la classe Thread qui implémente le threading. Les méthodes fournies par la classe Thread sont les suivantes -
run() - La méthode run () est le point d'entrée d'un thread.
start() - La méthode start () démarre un thread en appelant la méthode run.
join([time]) - Le join () attend la fin des threads.
isAlive() - La méthode isAlive () vérifie si un thread est toujours en cours d'exécution.
getName() - La méthode getName () renvoie le nom d'un thread.
setName() - La méthode setName () définit le nom d'un thread.
Création d'un fil à l'aide du module de filetage
Pour implémenter un nouveau thread à l'aide du module de threading, vous devez faire ce qui suit -
Définissez une nouvelle sous-classe de la classe Thread .
Remplacez la méthode __init __ (self [, args]) pour ajouter des arguments supplémentaires.
Ensuite, remplacez la méthode run (self [, args]) pour implémenter ce que le thread doit faire au démarrage.
Une fois que vous avez créé la nouvelle sous-classe Thread , vous pouvez en créer une instance, puis démarrer un nouveau thread en appelant la méthode start () , qui à son tour appelle la méthode run () .
Exemple
#!/usr/bin/python3
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, self.counter, 5)
print ("Exiting " + self.name)
def print_time(threadName, delay, counter):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print ("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print ("Exiting Main Thread")
Résultat
Lorsque nous exécutons le programme ci-dessus, il produit le résultat suivant -
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:00:21 2016
Thread-2: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:22 2016
Thread-1: Fri Feb 19 10:00:23 2016
Thread-2: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:24 2016
Thread-1: Fri Feb 19 10:00:25 2016
Exiting Thread-1
Thread-2: Fri Feb 19 10:00:26 2016
Thread-2: Fri Feb 19 10:00:28 2016
Thread-2: Fri Feb 19 10:00:30 2016
Exiting Thread-2
Exiting Main Thread
Synchronisation des threads
Le module de threading fourni avec Python comprend un mécanisme de verrouillage simple à implémenter qui vous permet de synchroniser les threads. Un nouveau verrou est créé en appelant la méthode Lock () , qui renvoie le nouveau verrou.
La méthode d' acquisition (blocage) du nouvel objet de verrouillage est utilisée pour forcer les threads à s'exécuter de manière synchrone. Le paramètre de blocage facultatif vous permet de contrôler si le thread attend pour acquérir le verrou.
Si le blocage est défini sur 0, le thread retourne immédiatement avec une valeur 0 si le verrou ne peut pas être acquis et avec un 1 si le verrou a été acquis. Si le blocage est défini sur 1, le thread se bloque et attend que le verrou soit libéré.
La méthode release () du nouvel objet de verrouillage est utilisée pour libérer le verrou lorsqu'il n'est plus nécessaire.
Exemple
#!/usr/bin/python3
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)
# Get lock to synchronize threads
threadLock.acquire()
print_time(self.name, self.counter, 3)
# Free lock to release next thread
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 = []
# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
# Start new Threads
thread1.start()
thread2.start()
# Add threads to thread list
threads.append(thread1)
threads.append(thread2)
# Wait for all threads to complete
for t in threads:
t.join()
print ("Exiting Main Thread")
Production
Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -
Starting Thread-1
Starting Thread-2
Thread-1: Fri Feb 19 10:04:14 2016
Thread-1: Fri Feb 19 10:04:15 2016
Thread-1: Fri Feb 19 10:04:16 2016
Thread-2: Fri Feb 19 10:04:18 2016
Thread-2: Fri Feb 19 10:04:20 2016
Thread-2: Fri Feb 19 10:04:22 2016
Exiting Main Thread
File d'attente prioritaire multithread
Le module File d'attente vous permet de créer un nouvel objet de file d'attente pouvant contenir un nombre spécifique d'éléments. Il existe des méthodes suivantes pour contrôler la file d'attente:
get() - Le get () supprime et renvoie un élément de la file d'attente.
put() - Le put ajoute un élément à une file d'attente.
qsize() - Le qsize () renvoie le nombre d'éléments qui sont actuellement dans la file d'attente.
empty()- Le vide () renvoie True si la file d'attente est vide; sinon, Faux.
full()- le full () renvoie True si la file d'attente est pleine; sinon, Faux.
Exemple
#!/usr/bin/python3
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
# Create new threads
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
# Fill the queue
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
# Wait for queue to empty
while not workQueue.empty():
pass
# Notify threads it's time to exit
exitFlag = 1
# Wait for all threads to complete
for t in threads:
t.join()
print ("Exiting Main Thread")
Production
Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -
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