Python - 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 supplémentaire; 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 l'endroit où, dans son contexte, il s'exécute actuellement.

  • 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.

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/python

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

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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'il soit très efficace pour le thread de bas niveau, le module de thread est très limité par rapport au module de thread 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/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

# Create new threads
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# Start new Threads
thread1.start()
thread2.start()

print "Exiting Main Thread"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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 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/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
      # 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"

Lorsque le code ci-dessus est exécuté, il produit le résultat suivant -

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 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/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

# 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"

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