Multithreading Python


Le multithreading est similaire à l'exécution de plusieurs programmes différents en même temps. Le multithreading présente les avantages suivants :

  • L'utilisation de threads peut mettre des tâches à long terme. programmes en traités en arrière-plan.

  • L'interface utilisateur peut être plus attrayante, de sorte que si l'utilisateur clique sur un bouton pour déclencher le traitement de certains événements, une barre de progression peut apparaître pour montrer la progression du traitement

  • La vitesse d'exécution du programme peut être accélérée

  • Les threads sont plus utiles dans la mise en œuvre de certaines tâches en attente telles que la saisie utilisateur, la lecture et l'écriture de fichiers , et l'envoi et la réception de données par le réseau, etc. Dans ce cas, nous pouvons libérer certaines ressources précieuses telles que l'utilisation de la mémoire, etc.

Les threads sont toujours différents des processus lors de l'exécution. Chaque thread indépendant possède un point d'entrée pour l'exécution du programme, une séquence d'exécution séquentielle et un point de sortie pour le programme. Cependant, les threads ne peuvent pas s'exécuter indépendamment et doivent exister dans le programme d'application, et le programme d'application fournit un contrôle d'exécution de plusieurs threads.

Chaque thread possède son propre ensemble de registres CPU, appelé contexte du thread, qui reflète l'état des registres CPU lors de la dernière exécution du thread.

Le registre du pointeur d'instruction et du pointeur de pile sont les deux registres les plus importants dans le contexte du thread. Le thread s'exécute toujours dans le contexte du processus. Ces adresses sont utilisées pour marquer la mémoire dans l'espace d'adressage du processus qui le possède. le fil.

  • Les fils de discussion peuvent être préemptés (interrompus).

  • Un thread peut être mis en attente (également appelé veille) pendant que d'autres threads sont en cours d'exécution - c'est ce qu'on appelle l'arrêt du thread.


Commencez à apprendre les threads Python

Il existe deux façons d'utiliser les threads en Python : des fonctions ou des classes pour envelopper des objets de thread.

Fonctionnel : Appelez la fonction start_new_thread() dans le module thread pour générer un nouveau thread. La syntaxe est la suivante :

thread.start_new_thread ( function, args[, kwargs] )

Description du paramètre :

  • fonction - fonction thread.

  • args - les paramètres passés à la fonction thread, il doit être de type tuple.

  • kwargs - paramètres facultatifs.

Exemple :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

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

Le résultat de l'exécution du programme ci-dessus est le 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

La fin du fil repose généralement sur le naturel fin de la fonction thread ; cela peut également être fait dans le thread. Lorsque thread.exit() est appelé dans la fonction, il lève une exception SystemExit pour quitter le thread.


Module Threading

Python fournit la prise en charge des threads via deux bibliothèques standards, thread et threading. thread fournit des threads primitifs de bas niveau et un simple verrou.

Autres méthodes fournies par le module thread :

  • threading.currentThread() : Renvoie la variable de thread actuelle.

  • threading.enumerate() : renvoie une liste contenant les threads en cours d'exécution. L'exécution fait référence au thread après son démarrage et avant sa fin, à l'exclusion des threads avant le démarrage et après la fin.

  • threading.activeCount() : renvoie le nombre de threads en cours d'exécution, qui a le même résultat que len(threading.enumerate()).

En plus des méthodes d'utilisation, le module thread fournit également la classe Thread pour gérer les threads. La classe Thread fournit les méthodes suivantes :

  • run() : Méthode utilisée pour représenter l'activité du thread.

  • start() :Démarrez l'activité du fil de discussion.


  • join([time]): Attendez que le fil de discussion se termine. Cela bloque le thread appelant jusqu'à ce que la méthode join() du thread soit appelée abort - quitte normalement ou lève une exception non gérée - ou qu'un délai d'attente facultatif se produise.

  • isAlive() : Renvoie si le fil de discussion est actif.

  • getName() : Renvoie le nom du fil de discussion.

  • setName(): Définissez le nom du fil de discussion.


Utilisez le module Threading pour créer un fil de discussion

Utilisez le module Threading pour créer un fil de discussion, héritez directement de threading.Thread, puis remplacez le __init__ méthode et méthode d'exécution :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import threading
import time

exitFlag = 0

class myThread (threading.Thread):   #继承父类threading.Thread
    def __init__(self, threadID, name, counter):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
    def run(self):                   #把要执行的代码写到run函数里面 线程在创建后会直接运行run函数 
        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:
            thread.exit()
        time.sleep(delay)
        print "%s: %s" % (threadName, time.ctime(time.time()))
        counter -= 1

# 创建新线程
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)

# 开启线程
thread1.start()
thread2.start()

print "Exiting Main Thread"

Les résultats d'exécution du programme ci-dessus sont les suivants

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

Si plusieurs threads modifient conjointement certaines données, résultats imprévisibles ; peut se produire. Afin de garantir l’exactitude des données, plusieurs threads doivent être synchronisés.

Une synchronisation simple des threads peut être obtenue à l'aide de Lock et Rlock de l'objet Thread. Les deux objets ont des méthodes d'acquisition et de libération. Pour les données qui ne nécessitent qu'un seul thread à la fois, ses opérations peuvent être placées entre l'acquisition et la libération. méthodes. Comme suit :

L'avantage du multi-threading est qu'il peut exécuter plusieurs tâches en même temps (du moins c'est ce que l'on ressent). Mais lorsque les threads doivent partager des données, des problèmes de désynchronisation des données peuvent survenir.

Considérons une situation : tous les éléments d'une liste sont à 0, le fil "set" change tous les éléments à 1 de l'arrière vers l'avant, et le fil "print" est responsable de la lecture de la liste d'avant en arrière et de l'impression.

Ensuite, peut-être que lorsque le thread "set" commencera à changer, le thread "print" imprimera la liste et la sortie sera moitié 0 et moitié 1. C'est la désynchronisation des données. Pour éviter cette situation, le concept de serrures a été introduit.

Les serrures ont deux états : verrouillé et déverrouillé. Chaque fois qu'un thread tel que « set » veut accéder aux données partagées, il doit d'abord obtenir le verrou ; si un autre thread tel que « print » a déjà obtenu le verrou, laissez le thread « set » faire une pause, ce qui est un blocage synchrone ; jusqu'à ce que le fil " Imprimer " Une fois l'accès terminé et le verrou libéré, laissez le fil " défini " continuer.

Après un tel traitement, lors de l'impression de la liste, soit tous les 0, soit tous les 1 seront imprimés, et il n'y aura plus de scène embarrassante de moitiés de 0 et moitié de 1.

Exemple :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

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
       # 获得锁,成功获得锁定后返回True
       # 可选的timeout参数不填时将一直阻塞直到获得锁定
       # 否则超时后将返回False
        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"

Thread Priority Queue (Queue)

Le module Queue de Python fournit des classes de file d'attente synchrones et sécurisées pour les threads, y compris FIFO (premier entré, premier sorti) file d'attente Queue, file d'attente LIFO (dernier entré, premier sorti), LifoQueue et file d'attente prioritaire PriorityQueue. Ces files d'attente implémentent des primitives de verrouillage et peuvent être utilisées directement en multi-threads. Les files d'attente peuvent être utilisées pour réaliser la synchronisation entre les threads.

Méthodes courantes dans le module Queue :


  • Queue.qsize() renvoie la taille de la file d'attente

  • Queue.empty() Si la file d'attente est vide, renvoie True, sinon False

  • Queue.full() Si la file d'attente est pleine, renvoie True, sinon False

  • Queue.full correspond à la taille maxsize

  • Queue.get([block[, timeout]]) récupère la file d'attente, délai d'attente

  • Queue.get_nowait() est équivalent à Queue.get (Faux)

  • Queue.put(item) écrit dans la file d'attente, le temps d'attente avant expiration

  • Queue.put_nowait(item) est équivalent à Queue.put(item, False)

  • Queue.task_done() Après avoir terminé une tâche, la fonction Queue.task_done() envoie un signal à la file d'attente où la tâche a été terminée

  • Queue. join() signifie en fait attendre que la file d'attente soit vide avant d'effectuer d'autres opérations

Exemple :

#!/usr/bin/python
# -*- coding: UTF-8 -*-

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"

Le résultat de l'exécution du programme ci-dessus :

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