Maison >développement back-end >Tutoriel Python >Introduction détaillée au multithreading en python (exemple de code)
Le contenu de cet article est une introduction détaillée (exemple de code) sur le multi-threading en Python. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer.
Cet article enregistre les problèmes rencontrés dans l'apprentissage de Python et quelques usages courants. Notez que la version Python de cet environnement de développement est la 2.7.
Lorsque vous nommez des fichiers Python, vous devez faire attention à ne pas entrer en conflit avec le nom de module par défaut du système, sinon une erreur sera signalée.
Comme dans l'exemple ci-dessous, lors de l'apprentissage des threads, le nom du fichier est threading.py
, et le script Python est tout à fait normal, mais l'erreur suivante est signalée : AttributeError: 'module' object has no attribute 'xxx'
.
threading.py
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_test.py @time: 18/8/25 09:14 """ import threading # 获取已激活的线程数 print(threading.active_count())
Exécution :
➜ baseLearn python threading/threading.py Traceback (most recent call last): File "threading/threading.py", line 9, in <module> import threading File "/Users/kaiyiwang/Code/python/baseLearn/threading/threading.py", line 12, in <module> print(threading.active_count()) AttributeError: 'module' object has no attribute 'active_count' ➜ baseLearn
Voir le fichier source de la bibliothèque import
et trouver le fichier source Existe et ne contient aucune erreur, et le fichier .pyc
du fichier source existe également Les mots, noms de modules, etc. sont les mêmes
de la bibliothèque (car le script py générera un fichier .pyc à chaque exécution ; une fois qu'il aura été généré. fichier pyc, si le code n'est pas mis à jour, pyc sera toujours utilisé lors de l'exécution, donc supprimez le fichier .pyc) et réexécutez le code ; ou trouvez un environnement capable d'exécuter le code, copiez et remplacez le fichier .pyc de la machine actuelle
.pyc
est rapide et facile à démarrer. dans cette section, nous vous apprendrons comment l'utiliser. threading_test.py
➜ baseLearn python threading/threading_test.py 1 ➜ baseLearn
threading
threading_test.py
Nous laissons
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_test.py @time: 18/8/25 09:14 """ import threading # 获取已激活的线程数 # print(threading.active_count()) # 查看所有线程信息 # print(threading.enumerate()) # 查看现在正在运行的线程 # print(threading.current_thread()) def thread_job(): print('This is a thread of %s' % threading.current_thread()) def main(): thread = threading.Thread(target=thread_job,) # 定义线程 thread.start() # 让线程开始工作 if __name__ == '__main__': main()
Le résultat de sortie attendu est d'exécuter dans l'ordre : T1
Mais le résultat réel de l'exécution est : threading_join.py
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_join.py @time: 18/8/25 09:14 """ import threading import time def thread_job(): print('T1 start\n') for i in range(10): time.sleep(0.1) # 任务时间0.1s print("T1 finish\n") def main(): added_thread = threading.Thread(target=thread_job, name='T1') # 定义线程 added_thread.start() # 让线程开始工作 print("all done\n") if __name__ == '__main__': main()Le résultat de l'ajout de join()La tâche de thread est générée avant d'être terminée
T1 start T1 finish all done. Si vous souhaitez suivre l'ordre, vous pouvez appeler
sur le fil après l'avoir démarré :
➜ baseLearn python threading/threading_join.py T1 start all done T1 finish ➜ baseLearn
all done
Fichier script complet : join
added_thread.start() added_thread.join() print("all done\n")Essayez-leSi vous ajoutez deux fils, quel sera le résultat imprimé ?
➜ baseLearn python threading/threading_join.py T1 start T1 finish all doneLe résultat "un" est :
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_join.py @time: 18/8/25 09:14 """ import threading import time def thread_job(): print('T1 start\n') for i in range(10): time.sleep(0.1) # 任务时间0.1s print("T1 finish\n") def main(): added_thread = threading.Thread(target=thread_job, name='T1') # 定义线程 added_thread.start() # 让线程开始工作 added_thread.join() print("all done\n") if __name__ == '__main__': main()Maintenant, ni T1 ni T2 n'ont
Notez que "un" est dit ici à cause de l'apparence de tous. done Cela dépend entièrement de la vitesse d'exécution des deux threads. Il est tout à fait possible que la finition T2 apparaisse après tout. Cette méthode d'exécution désordonnée nous est intolérable, nous devons donc utiliser join pour la contrôler.
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_join.py @time: 18/8/25 09:14 """ import threading import time def T1_job(): print('T1 start\n') for i in range(10): time.sleep(0.1) # 任务时间0.1s print("T1 finish\n") def T2_job(): print("T2 start\n") print("T2 finish\n") def main(): thread_1 = threading.Thread(target=T1_job, name='T1') # 定义线程 thread_2 = threading.Thread(target=T2_job, name='T2') # 定义线程 thread_1.start() # 开启T1 thread_2.start() # 开启T2 print("all done\n") if __name__ == '__main__': main()Essayons d'ajouter
:
T1 start T2 start T2 finish all done T1 finish
après le début de T1 et avant le début de T2 Imprimez le résultat : join
Vous pouvez voir que T2 attendra. pour que T1 se termine avant de commencer à fonctionner. thread_1.join()
thread_1.start() thread_1.join() # notice the difference! thread_2.start() print("all done\n")
Fonction d'implémentation
T1 start T1 finish T2 start all done T2 finishFonction d'implémentation de code, transmettre les données dans la liste de données, utiliser quatre threads pour traiter et enregistrer le résultat dans la file d'attente, une fois le thread exécuté, obtenez le résultat stocké de la file d'attente Définissez un
Queue
Lors de l'exécution du script ci-dessus, une erreur s'est produite : 代替return
Après vérification de la raison, cela est dû à la version python : threading_queue.py
Solution : Aucun module nommé 'Queue'
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_queue.py @time: 18/8/25 09:14 """ import threading import time from queue import Queue def job(l, q): for i in range(len(l)): l[i] = l[i] ** 2 q.put(l) #多线程调用的函数不能用return返回值 def multithreading(): q = Queue() #q中存放返回值,代替return的返回值 threads = [] data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]] for i in range(4): #定义四个线程 t = threading.Thread(target=job, args=(data[i], q)) #Thread首字母要大写,被调用的job函数没有括号,只是一个索引,参数在后面 t.start() #开始线程 threads.append(t) #把每个线程append到线程列表中 for thread in threads: thread.join() results = [] for _ in range(4): results.append(q.get()) #q.get()按顺序从q中拿出一个值 print(results) if __name__ == '__main__': multithreading()Sur Python 2, le module s'appelle Queue, sur Python 3, il a été renommé pour suivre PEP8 directives (toutes en minuscules pour les noms de modules), ce qui la met en file d'attente. La classe reste en file d'attente sur toutes les versions (après PEP8).En général, la façon dont vous écririez les importations portables de version serait de faire :
➜ baseLearn python threading/threading_queue.py Traceback (most recent call last): File "threading/threading_queue.py", line 11, in <module> from queue import Queue ImportError: No module named queue
aimer ça en python3 Citation :
En python2 on peut citer comme ceci :
Imprimer :try: import queue except ImportError: import Queue as queueCode complet :
from Queue import Queue4. Problème d'efficacité du GIL
baseLearn python ./threading/threading_queue.py [[1, 4, 9], [9, 16, 25], [16, 16, 16], [25, 25, 25]]
Qu'est-ce que le GIL ? threading_queue.py
Cette fois, nous verrons pourquoi le multi-threading de Python n'est parfois pas particulièrement idéal. La raison principale est qu'il existe un lien nécessaire dans la conception de Python, qui est
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_queue.py @time: 18/8/25 09:14 """ import threading # import time from Queue import Queue def job(l, q): for i in range(len(l)): l[i] = l[i] ** 2 q.put(l) #多线程调用的函数不能用return返回值 def multithreading(): q = Queue() #q中存放返回值,代替return的返回值 threads = [] data = [[1,2,3],[3,4,5],[4,4,4],[5,5,5]] for i in range(4): #定义四个线程 t = threading.Thread(target=job, args=(data[i], q)) #Thread首字母要大写,被调用的job函数没有括号,只是一个索引,参数在后面 t.start() #开始线程 threads.append(t) #把每个线程append到线程列表中 for thread in threads: thread.join() results = [] for _ in range(4): results.append(q.get()) #q.get()按顺序从q中拿出一个值 print(results) if __name__ == '__main__': multithreading()
我们创建一个 job
, 分别用 threading 和 一般的方式执行这段程序. 并且创建一个 list 来存放我们要处理的数据. 在 Normal 的时候, 我们这个 list 扩展4倍, 在 threading 的时候, 我们建立4个线程, 并对运行时间进行对比.
threading_gil.py
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_gil.py @time: 18/8/25 09:14 """ import threading from Queue import Queue import copy import time def job(l, q): res = sum(l) q.put(l) #多线程调用的函数不能用return返回值 def multithreading(l): q = Queue() #q中存放返回值,代替return的返回值 threads = [] for i in range(4): #定义四个线程 t = threading.Thread(target=job, args=(copy.copy(l), q), name="T%i" % i) #Thread首字母要大写,被调用的job函数没有括号,只是一个索引,参数在后面 t.start() #开始线程 threads.append(t) #把每个线程append到线程列表中 [t.join() for t in threads] total = 0 for _ in range(4): total = q.get() #q.get()按顺序从q中拿出一个值 print(total) def normal(l): total = sum(l) print(total) if __name__ == '__main__': l = list(range(1000000)) s_t = time.time() normal(l*4) print('normal:', time.time() - s_t) s_t = time.time() multithreading(l) print('multithreading: ', time.time() - s_t)
如果你成功运行整套程序, 你大概会有这样的输出. 我们的运算结果没错, 所以程序 threading 和 Normal 运行了一样多次的运算. 但是我们发现 threading 却没有快多少, 按理来说, 我们预期会要快3-4倍, 因为有建立4个线程, 但是并没有. 这就是其中的 GIL 在作怪.
1999998000000 normal: 0.10034608840942383 1999998000000 multithreading: 0.08421492576599121
threading_lock.py
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_lock.py @time: 18/8/25 09:14 """ import threading # 全局变量A的值每次加1,循环10次,并打印 def job1(): global A for i in range(10): A+=1 print('job1',A) # 全局变量A的值每次加10,循环10次,并打印 def job2(): global A for i in range(10): A+=10 print('job2',A) # 定义两个线程,分别执行函数一和函数二 if __name__== '__main__': A=0 t1=threading.Thread(target=job1) t2=threading.Thread(target=job2) t1.start() t2.start() t1.join() t2.join()
打印输出数据:
➜ baseLearn python ./threading/threading_lock.py ('job1', ('job2'1) , (11)'job1' ('job2', 22) ('job2', 32) ('job2', 42) ('job2', 52) ('job2', 62) ('job2', 72) ('job2', 82) ('job2', 92) ('job2', 102) , 12) ('job1', 103) ('job1', 104) ('job1', 105) ('job1', 106) ('job1', 107) ('job1', 108) ('job1', 109) ('job1', 110)
可以看出,打印的结果非常混乱
lock在不同线程使用同一共享内存
时,能够确保线程之间互不影响,使用lock的方法是, 在每个线程执行运算修改共享内存之前,执行lock.acquire()
将共享内存上锁, 确保当前线程执行时,内存不会被其他线程访问,执行运算完毕后,使用lock.release()
将锁打开, 保证其他的线程可以使用该共享内存。
函数一和函数二加锁
def job1(): global A,lock lock.acquire() for i in range(10): A+=1 print('job1',A) lock.release() def job2(): global A,lock lock.acquire() for i in range(10): A+=10 print('job2',A) lock.release()
主函数中定义一个Lock
if __name__== '__main__': lock=threading.Lock() A=0 t1=threading.Thread(target=job1) t2=threading.Thread(target=job2) t1.start() t2.start() t1.join() t2.join()
完整代码:
# -*- coding:utf-8 -*- """ @author: Corwien @file: threading_lock.py @time: 18/8/25 09:14 """ import threading def job1(): global A,lock lock.acquire() for i in range(10): A+=1 print('job1',A) lock.release() def job2(): global A,lock lock.acquire() for i in range(10): A+=10 print('job2',A) lock.release() if __name__== '__main__': lock = threading.Lock() A=0 t1=threading.Thread(target=job1) t2=threading.Thread(target=job2) t1.start() t2.start() t1.join() t2.join()
打印输出:
➜ baseLearn python ./threading/threading_lock.py ('job1', 1) ('job1', 2) ('job1', 3) ('job1', 4) ('job1', 5) ('job1', 6) ('job1', 7) ('job1', 8) ('job1', 9) ('job1', 10) ('job2', 20) ('job2', 30) ('job2', 40) ('job2', 50) ('job2', 60) ('job2', 70) ('job2', 80) ('job2', 90) ('job2', 100) ('job2', 110)
从打印结果来看,使用lock
后,一个一个线程执行完。使用lock
和不使用lock,最后打印输出的结果是不同的。
相关推荐:
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!