莫烦Python笔记__Python多线程技巧
1.threading的四种基本操作
a.查看已激活线程
b.查看所有线程信息
c.查看正在运行的线程信息
d.添加线程
import threading """ 。 介绍threading的基本功能; 。 添加线程操作; """ #查看已激活现成 print(threading.active_count()) #查看所有现成信息 print(threading.enumerate()) #查看正在运行的线程信息 print(threading.current_thread()) #添加线程 def thread_job(): print("This is a added thread, number is %s"%(threading.current_thread)) def main(): added_thread = threading.Thread(target=thread_job,name="Luna") added_thread.start() print(threading.active_count()) print(threading.enumerate()) if __name__ == '__main__': main()
显示结果:
1 [<_MainThread(MainThread, started 2236)>] <_MainThread(MainThread, started 2236)> This is a added thread, number is <function current_thread at 0x000001CE6B067400> 2 [<_MainThread(MainThread, started 2236)>] [Finished in 0.5s]
2.threading的join()操作__join:将被join的线程加入主线程
a.没有join操作
import threading import time """ join函数:可以让join()的现成先完成后在进行后续的工作 """ def thread_job1(): print("T1 started...\n") print(threading.current_thread) for i in range(10): time.sleep(0.1) print("T1 finished...\n") def thread_job2(): print("T2 started...\n") print(threading.current_thread) print("T2 finished ...\n") def main(): added_thread1 = threading.Thread(target=thread_job1,name='T1') added_thread2 = threading.Thread(target=thread_job2,name='T2') added_thread1.start() added_thread2.start() #必须在add_thread1.join完成后才可执行后面的 #可屏蔽测试没有join功能时的情况 # added_thread1.join(timeout=None) # added_thread2.join(timeout=None) print(threading.enumerate()) print("目前总线程数量:{}".format(str(threading.active_count))) print("All done") if __name__ == '__main__': main()
结果:
T1 started... <function current_thread at 0x000001B569FA7378> T2 started... [<_MainThread(MainThread, started 14804)>, <Thread(T1, started 1248)>, <Thread(T2, started 14528)>] 目前总线程数量:<function active_count at 0x000001B569FA8510> <function current_thread at 0x000001B569FA7378> All done T2 finished ... T1 finished...
可以看出:先进行T1>>T2>>主线程>>T2先完成>>T1先完成
b.进行join()操作
import threading import time """ join函数:可以让join()的现成先完成后在进行后续的工作 """ def thread_job1(): print("T1 started...\n") print(threading.current_thread) for i in range(10): time.sleep(0.1) print("T1 finished...\n") def thread_job2(): print("T2 started...\n") print(threading.current_thread) print("T2 finished ...\n") def main(): added_thread1 = threading.Thread(target=thread_job1,name='T1') added_thread2 = threading.Thread(target=thread_job2,name='T2') added_thread1.start() added_thread2.start() #必须在add_thread1.join完成后才可执行后面的 #可屏蔽测试没有join功能时的情况 added_thread1.join(timeout=None) added_thread2.join(timeout=None) print(threading.enumerate()) print("目前总线程数量:{}".format(str(threading.active_count))) print("All done") if __name__ == '__main__': main()
结果
T1 started... <function current_thread at 0x000002CF02FE7400> T2 started... <function current_thread at 0x000002CF02FE7400> T2 finished ... T1 finished... [<_MainThread(MainThread, started 8204)>] 目前总线程数量:<function active_count at 0x000002CF02FE8598> All done [Finished in 1.2s]
可看出 先完成T2(没有延迟)>>T1>>最后ALL DONE
3.Queue来进行存储(线程不能return,只能q.put(), 主程序中q.get())
以前的线程都是没有返回值的
import threading import time from queue import Queue import numpy as np """ Queue: 代码实现功能,将数据列表中的数据传入, 使用四个线程处理,将结果保存在Queue 中,线程执行完后,从Queue中获取存储 的结果 """ #定义一个被多线程调用的任务 def thread_job(l,q): for i in range(len(l)): l[i] = l[i]**2 #注:多线程调用的函数不能用return返回值 q.put(l) def multithreading(): #q中存放返回值,用来代替return q = Queue() threads = [] # data = np.array([[1,2,3],[2,3,4],[3,3,3],[4,5,6]]) # data.tolist() data = [[1,2,3],[2,3,4],[3,3,3],[4,5,6]] #results用来保存结果 results = [] #定义四个线程 #每个线程分别算data列表中的一个子列表 for i in range(4): t = threading.Thread(target=thread_job,args=(data[i],q)) t.start() threads.append(t) #分别join四个线程到主线程 for thread in threads: thread.join() #results保存结果 for _ in range(4): #q.get()获取结果 results.append(q.get()) print(results) if __name__ == '__main__': multithreading()
结果:
[[1, 4, 9], [4, 9, 16], [9, 9, 9], [16, 25, 36]] [Finished in 0.3s]
P.S 线程其实并不能真正的加速计算过程____GIL机制
线面的程序用来具体测试加入线程和不加入线程的程序运行时间
GIL机制:
Global Interpreter Lock(GIL)
这个东西让 Python 还是一次性只能处理一个东西.
尽管Python完全支持多线程编程, 但是解释器的C语言实现部分在完全并行执行时并不是线程安全的。 实际上,解释器被一个全局解释器锁保护着,它确保任何时候都只有一个Python线程执行。 GIL最大的问题就是Python的多线程程序并不能利用多核CPU的优势 (比如一个使用了多个线程的计算密集型程序只会在一个单CPU上面运行)。
在讨论普通的GIL之前,有一点要强调的是GIL只会影响到那些严重依赖CPU的程序(比如计算型的)。 如果你的程序大部分只会涉及到I/O,比如网络交互,那么使用多线程就很合适, 因为它们大部分时间都在等待。实际上,你完全可以放心的创建几千个Python线程, 现代操作系统运行这么多线程没有任何压力,没啥可担心的。(不适用于CPU密集型计算)
多线程与多进程:
python多线程适合io操作密集型的任务(如socket server 网络并发这一类的);
python多线程不适合cpu密集操作型的任务,主要使用cpu来计算,如大量的数学计算。
那么如果有cpu密集型的任务怎么办,可以通过多进程来操作(不是多线程)。
假如CPU有8核,每核CPU都可以用1个进程,每个进程可以用1个线程来进行计算。
多进程适合在CPU 密集型操作(cpu 操作指令比较多,如科学计算,位数多的浮点运算)
多线程适合在IO 密集型操作(读写数据操作较多的,比如爬虫)
进程与线程的区别:
线程是并发,进程是并行;进程之间相互独立,是系统分配资源的最小单位,同一个进程中的所有线程共享资源。
进程:一个运行的程序或代码就是一个进程,一个没有运行的代码叫程序。
进程是系统进行资源分配的最小单位,进程拥有自己的内存空间,所以进程间数据不共享,开销大。
线程:调度执行的最小单位,也叫执行路径,不能独立存在,依赖进程的存在而存在,一个进程至少有一个线程,叫主线程,多个线程共享内存(数据共享和全局变量),因此提升程序的运行效率。
协程:用户态的轻量级线程,调度有用户控制,拥有自己的寄存器上下文和栈,切换基本没有内核切换的开销,切换灵活。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 我们创建一个 job, 分别用 threading 和 一般的方式执行这段程序. 2 # 并且创建一个 list 来存放我们要处理的数据. 在 Normal 的时候, 3 # 我们这个 list 扩展4倍,在 threading 的时候, 我们建立4个线程, 4 # 并对运行时间进行对比. 5 6 import threading 7 from queue import Queue 8 import copy 9 import time 10 11 def job(l, q): 12 res = sum(l) 13 q.put(res) 14 15 def multithreading(l): 16 q = Queue() 17 threads = [] 18 for i in range(4): 19 t = threading.Thread(target=job, args=(copy.copy(l), q), name='T%i' % i) 20 t.start() 21 threads.append(t) 22 [t.join() for t in threads] 23 total = 0 24 for _ in range(4): 25 total += q.get() 26 print(total) 27 28 def normal(l): 29 total = sum(l) 30 print(total) 31 32 if __name__ == '__main__': 33 l = list(range(1000000)) 34 s_t = time.time() 35 normal(l*4) 36 print('normal: ',time.time()-s_t) 37 s_t = time.time() 38 multithreading(l) 39 print('multithreading: ', time.time()-s_t)
5.Lock功能,锁住某线程,使其进行完之后才进行下一个线程
#进程锁 #先运行一个线程,在运行另一个线程 import threading import time from queue import Queue #没有线程锁 def job1_withoutlock(): global A for i in range(10): A += 1 print('job1:',A) def job2_withoutlock(): global A for i in range(10): A+=10 print('job2:',A) #有线程锁 def job3_withlock(): global B,lock lock.acquire() for i in range(10): B+=1 print(B) lock.release() def job4_withlock(): global B,lock lock.acquire() for i in range(10): B+=10 print(B) lock.release() if __name__ == '__main__': #全局变量A A = 0 B = 0 lock = threading.Lock() print("没有lock锁...\n") lock_without1 = threading.Thread(target=job1_withoutlock,name='withoutlock1') lock_without2 = threading.Thread(target=job2_withoutlock,name='withoutlock2') lock_without1.start() lock_without2.start() lock_without1.join() lock_without2.join() print("有lock锁...\n") lock_with1 = threading.Thread(target=job3_withlock,name='withlock3') lock_with2 = threading.Thread(target=job4_withlock,name='withlock4') lock_with1.start() lock_with2.start() lock_with1.join() lock_with2.join()
没有Lock: job1job2 11 job2 21 job2 31 job2 41 job2 51 job2 61 job2 71 job2 81 job2 91 job2 101 1 job1 102 job1 103 job1 104 job1 105 job1 106 job1 107 job1 108 job1 109 job1 110 Lock: 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