programmation multithread
En fait, après la création d'un thread, le thread ne conserve pas toujours un état. Son état est à peu près le suivant?:
Nouveau créé
Runnable ready. En attente de planification
En cours d'exécution
Bloqué. Un blocage peut survenir en mode Wait Locked Sleeping
Dead
Les threads ont différents états et différents types. Il peut être grossièrement divisé en?:
Thème principal
Sous-thread
Thème démon (thread d'arrière-plan)
Thème de premier plan
Après une brève compréhension de ceux-ci, nous commen?ons à examiner l'utilisation spécifique du code.
1. Création de threads
Python fournit deux modules pour les opérations multithread, à savoir le thread et le threading
Le premier est un module de niveau relativement bas, utilisé pour les opérations de niveau inférieur, et n'est généralement pas adapté. pour le développement au niveau des applications. Couramment utilisé.
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import time import threading class MyThread(threading.Thread): def run(self): for i in range(5): print('thread {}, @number: {}'.format(self.name, i)) time.sleep(1) def main(): print("Start main threading") # 創(chuàng)建三個(gè)線程 threads = [MyThread() for i in range(3)] # 啟動(dòng)三個(gè)線程 for t in threads: t.start() print("End Main threading") if __name__ == '__main__': main()
Résultats d'exécution?:
Start main threading thread Thread-1, @number: 0 thread Thread-2, @number: 0 thread Thread-3, @number: 0 End Main threading thread Thread-2, @number: 1 thread Thread-1, @number: 1 thread Thread-3, @number: 1 thread Thread-1, @number: 2 thread Thread-3, @number: 2 thread Thread-2, @number: 2 thread Thread-2, @number: 3 thread Thread-3, @number: 3 thread Thread-1, @number: 3 thread Thread-3, @number: 4 thread Thread-2, @number: 4 thread Thread-1, @number: 4
Notez que les résultats de sortie des différents environnements ici sont définitivement différents.
2. Fusion de threads (méthode de jointure)
D'après les résultats imprimés dans l'exemple ci-dessus, une fois le thread principal terminé, le thread enfant est toujours en cours d'exécution. Nous avons donc besoin que le thread principal attende que le thread enfant ait fini de s'exécuter avant de quitter. Que devons-nous faire ?
à ce stade, vous devez utiliser la méthode de jointure.
Dans l'exemple ci-dessus, ajoutez un morceau de code comme suit?:
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import time import threading class MyThread(threading.Thread): def run(self): for i in range(5): print('thread {}, @number: {}'.format(self.name, i)) time.sleep(1) def main(): print("Start main threading") # 創(chuàng)建三個(gè)線程 threads = [MyThread() for i in range(3)] # 啟動(dòng)三個(gè)線程 for t in threads: t.start() # 一次讓新創(chuàng)建的線程執(zhí)行 join for t in threads: t.join() print("End Main threading") if __name__ == '__main__': main()
D'après les résultats imprimés, il est clairement visible que par rapport aux résultats imprimés dans l'exemple ci-dessus, le thread principal se termine après avoir attendu que le thread enfant finir de courir.
Start main threading thread Thread-1, @number: 0 thread Thread-2, @number: 0 thread Thread-3, @number: 0 thread Thread-1, @number: 1 thread Thread-3, @number: 1 thread Thread-2, @number: 1 thread Thread-2, @number: 2 thread Thread-1, @number: 2 thread Thread-3, @number: 2 thread Thread-2, @number: 3 thread Thread-1, @number: 3 thread Thread-3, @number: 3 thread Thread-3, @number: 4 thread Thread-2, @number: 4 thread Thread-1, @number: 4 End Main threading
3. Synchronisation des threads et verrouillage mutex
L'utilisation du chargement de threads pour obtenir des données entra?ne généralement une désynchronisation des données. Bien s?r, nous pouvons verrouiller la ressource à ce moment-là, c'est-à-dire que le thread accédant à la ressource doit obtenir le verrou avant d'y accéder.
Le module de threading nous offre une fonction Lock.
lock = threading.Lock()
Acquérir le verrou dans le thread
lock.acquire()
Après utilisation, nous devons absolument libérer le verrou
lock.release()
Bien s?r, afin de prendre en charge plusieurs requêtes pour la même ressource dans le même thread, Python fournit un verrou réentrant (RLock ). RLock maintient en interne un verrou et une variable de compteur. Le compteur enregistre le nombre d'acquisitions, de sorte que la ressource puisse être requise plusieurs fois. Jusqu'à ce que toutes les acquisitions d'un thread soient libérées, d'autres threads peuvent obtenir la ressource.
Alors comment créer un verrou de réentrée ? C'est aussi une question de code?:
r_lock = threading.RLock()
4. Variable de condition de condition
Les verrous pratiques peuvent réaliser la synchronisation des threads, mais dans des environnements plus complexes, certains jugements conditionnels doivent être portés sur les verrous. Python fournit des objets Condition. L'objet Condition peut être utilisé pour traiter les données une fois que certains événements sont déclenchés ou que des conditions spécifiques sont remplies. En plus de la méthode d'acquisition et de la méthode de libération de l'objet Lock, Condition fournit également des méthodes d'attente et de notification. Le thread acquiert d’abord un verrou de variable de condition. Si les conditions sont insuffisantes, le thread attend. Si les conditions sont remplies, le thread est exécuté et il peut même notifier les autres threads. Les autres threads en état d'attente réévalueront les conditions après avoir re?u la notification.
La variable de condition peut être vue comme différents threads acquérant le verrou les uns après les autres. Si la condition n'est pas remplie, elle peut être comprise comme étant jetée dans un pool d'attente (Lock ou RLock). Informez directement les autres threads, puis réévaluez la condition. Ce processus est répété en continu pour résoudre des problèmes de synchronisation complexes.
Ce modèle est souvent utilisé dans le modèle producteur-consommateur. Jetez un ?il aux exemples suivants d'acheteurs et de vendeurs d'achats en ligne?:
#!/usr/bin/env python3 # -*- coding: UTF-8 -*- import threading, time class Consumer(threading.Thread): def __init__(self, cond, name): # 初始化 super(Consumer, self).__init__() self.cond = cond self.name = name def run(self): # 確保先運(yùn)行Seeker中的方法 time.sleep(1) self.cond.acquire() print(self.name + ': 我這兩件商品一起買,可以便宜點(diǎn)嗎') self.cond.notify() self.cond.wait() print(self.name + ': 我已經(jīng)提交訂單了,你修改下價(jià)格') self.cond.notify() self.cond.wait() print(self.name + ': 收到,我支付成功了') self.cond.notify() self.cond.release() print(self.name + ': 等待收貨') class Producer(threading.Thread): def __init__(self, cond, name): super(Producer, self).__init__() self.cond = cond self.name = name def run(self): self.cond.acquire() # 釋放對(duì)瑣的占用,同時(shí)線程掛起在這里,直到被 notify 并重新占有瑣。 self.cond.wait() print(self.name + ': 可以的,你提交訂單吧') self.cond.notify() self.cond.wait() print(self.name + ': 好了,已經(jīng)修改了') self.cond.notify() self.cond.wait() print(self.name + ': 嗯,收款成功,馬上給你發(fā)貨') self.cond.release() print(self.name + ': 發(fā)貨商品') cond = threading.Condition() consumer = Consumer(cond, '買家(兩點(diǎn)水)') producer = Producer(cond, '賣家(三點(diǎn)水)') consumer.start() producer.start()
Les résultats de sortie sont les suivants?:
買家(兩點(diǎn)水): 我這兩件商品一起買,可以便宜點(diǎn)嗎 賣家(三點(diǎn)水): 可以的,你提交訂單吧 買家(兩點(diǎn)水): 我已經(jīng)提交訂單了,你修改下價(jià)格 賣家(三點(diǎn)水): 好了,已經(jīng)修改了 買家(兩點(diǎn)水): 收到,我支付成功了 買家(兩點(diǎn)水): 等待收貨 賣家(三點(diǎn)水): 嗯,收款成功,馬上給你發(fā)貨 賣家(三點(diǎn)水): 發(fā)貨商品
5. communication
Si le programme contient plusieurs threads, et ces threads doivent inévitablement communiquer entre eux. Alors, comment pouvons-nous échanger des informations ou des données en toute sécurité entre ces threads??
Le moyen le plus s?r d'envoyer des données d'un thread à un autre est probablement d'utiliser une file d'attente de la bibliothèque de files d'attente. Crée un objet Queue partagé par plusieurs threads, qui ajoutent ou suppriment des éléments de la file d'attente à l'aide des opérations put() et get().
# -*- coding: UTF-8 -*- from queue import Queue from threading import Thread isRead = True def write(q): # 寫數(shù)據(jù)進(jìn)程 for value in ['兩點(diǎn)水', '三點(diǎn)水', '四點(diǎn)水']: print('寫進(jìn) Queue 的值為:{0}'.format(value)) q.put(value) def read(q): # 讀取數(shù)據(jù)進(jìn)程 while isRead: value = q.get(True) print('從 Queue 讀取的值為:{0}'.format(value)) if __name__ == '__main__': q = Queue() t1 = Thread(target=write, args=(q,)) t2 = Thread(target=read, args=(q,)) t1.start() t2.start()
Les résultats de sortie sont les suivants?:
寫進(jìn) Queue 的值為:兩點(diǎn)水 寫進(jìn) Queue 的值為:三點(diǎn)水 從 Queue 讀取的值為:兩點(diǎn)水 寫進(jìn) Queue 的值為:四點(diǎn)水 從 Queue 讀取的值為:三點(diǎn)水 從 Queue 讀取的值為:四點(diǎn)水
Python fournit également l'objet Event pour la communication entre les threads. Il s'agit d'un indicateur de signal défini par le thread. Si l'indicateur de signal est vrai, les autres threads attendent que le signal soit contacté. .
L'objet Event implémente un mécanisme de communication de thread simple. Il fournit des signaux de configuration, d'effacement, d'attente, etc. pour réaliser la communication entre les threads.
Définir le signal
Utilisez la méthode set() de Event pour définir l'indicateur de signal à l'intérieur de l'objet Event sur true. L'objet Event fournit la méthode isSe() pour déterminer l'état de son indicateur de signal interne. Lorsque vous utilisez la méthode set() de l'objet événement, la méthode isSet() renvoie true
Effacer le signal
Utilisez la méthode clear() de l'objet Event pour effacer l'indicateur de signal à l'intérieur de l'objet Event, c'est-à-dire définissez-le sur false. Lors de l'utilisation de la méthode Event After clear, la méthode isSet() renvoie false
Waiting
La méthode wait de l'objet Event ne s'exécutera rapidement et terminera le retour que lorsque le signal interne est vrai. Lorsque l'indicateur de signal interne de l'objet Event est faux, la méthode wait attend qu'il soit vrai avant de revenir.
Exemple?:
# -*- coding: UTF-8 -*- import threading class mThread(threading.Thread): def __init__(self, threadname): threading.Thread.__init__(self, name=threadname) def run(self): # 使用全局Event對(duì)象 global event # 判斷Event對(duì)象內(nèi)部信號(hào)標(biāo)志 if event.isSet(): event.clear() event.wait() print(self.getName()) else: print(self.getName()) # 設(shè)置Event對(duì)象內(nèi)部信號(hào)標(biāo)志 event.set() # 生成Event對(duì)象 event = threading.Event() # 設(shè)置Event對(duì)象內(nèi)部信號(hào)標(biāo)志 event.set() t1 = [] for i in range(10): t = mThread(str(i)) # 生成線程列表 t1.append(t) for i in t1: # 運(yùn)行線程 i.start()
Le résultat de sortie est le suivant?:
1 0 3 2 5 4 7 6 9 8
6. Fil d'arrière-plan
Par défaut, après la sortie du thread principal, même si le thread enfant ne se joint pas. Ensuite, une fois le thread principal terminé, le thread enfant continuera à s'exécuter. Si vous souhaitez que le thread principal se ferme, ses sous-threads se fermeront également et ne s'exécuteront plus, vous devez définir les sous-threads comme threads d'arrière-plan. Python fournit la méthode setDeamon.