线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁...
方式一: 1 from threading import Thread 2 def f1(i): 3 print(i) 4 if __name__ == '__main__': 5 for i in range(10): 6 t = Thread(target=f1,args=(i,)) 7 t.start() 8 print('主線程')
方式二:
1 from threading import Thread 2 class MyThread(Thread): 3 def __init__(self, i): 4 super().__init__() 5 self.i = i 6 7 def run(self): 8 print('%s哈哈' % self.i) 9 10 if __name__ == '__main__': 11 for i in range(10): 12 t = MyThread(i) 13 t.start() 14 print('主線程')線程之間數(shù)據(jù)共享的驗(yàn)證:
1 import time 2 from threading import Thread 3 num = 100 4 def f1(): 5 global num 6 num = 33 7 time.sleep(2) 8 print('num>>>',num) 9 if __name__ == '__main__': 10 t = Thread(target=f1,) 11 t.start() 12 print('主線程的num',num) 多線程跟多進(jìn)程的效率對(duì)比:結(jié)論:當(dāng)遇到io阻塞情況,多線程比多進(jìn)程效率高,當(dāng)遇到大量計(jì)算情況,多進(jìn)程比多線程效率高 1 import time 2 from threading import Thread 3 from multiprocessing import Process 4 def f1(): 5 # time.sleep(1) 6 n = 10 7 for i in range(10000000): 8 n += i 9 if __name__ == '__main__': 10 t_s_time = time.time() 11 t_list = [] 12 for i in range(10): 13 t = Thread(target=f1,) 14 t.start() 15 t_list.append(t) 16 [tt.join() for tt in t_list] 17 t_e_time = time.time() 18 t_dif_time = t_e_time - t_s_time 19 20 p_s_time = time.time() 21 p_list = [] 22 for i in range(10): 23 p = Process(target=f1,) 24 p.start() 25 p_list.append(p) 26 [pp.join() for pp in p_list] 27 p_e_time = time.time() 28 p_dif_time = p_e_time - p_s_time 29 print('線程的時(shí)間:',t_dif_time) 30 print('進(jìn)程的時(shí)間:',p_dif_time) 線程鎖(互斥鎖): 作用:犧牲了效率,保證了數(shù)據(jù)安全 1 import time 2 from threading import Thread,Lock 3 num = 100 4 def f1(t_lock): 5 t_lock.acquire() 6 global num 7 tme = num 8 tme -= 1 9 num = tme 10 time.sleep(1) 11 print('子線程的num',num) 12 t_lock.release() 13 if __name__ == '__main__': 14 t_lock = Lock() 15 t_list = [] 16 for i in range(10): 17 t = Thread(target=f1,args=(t_lock,)) 18 t.start() 19 t_list.append(t) 20 [tt.join() for tt in t_list] 21 print('主線程的num',num) 死鎖現(xiàn)象:出現(xiàn)在鎖嵌套的時(shí)候,雙方互相搶對(duì)方已經(jīng)拿到的鎖,導(dǎo)致雙方互相等待,程序卡住(重點(diǎn)) 死鎖模擬: 1 import time 2 from threading import Thread,Lock 3 def f1(t_lockA,t_lockB): 4 t_lockA.acquire() 5 print('f1搶到了A鎖') 6 time.sleep(1) 7 t_lockB.acquire() 8 print('f1搶到了B鎖') 9 t_lockB.release() 10 t_lockA.release() 11 def f2(t_lockA,t_lockB): 12 t_lockB.acquire() 13 print('f2搶到了B鎖') 14 time.sleep(1) 15 t_lockA.acquire() 16 print('f2搶到了A鎖') 17 t_lockA.release() 18 t_lockB.release() 19 if __name__ == '__main__': 20 t_lockA = Lock() 21 t_lockB = Lock() 22 t1 = Thread(target=f1,args=(t_lockA,t_lockB)) 23 t2 = Thread(target=f2,args=(t_lockA,t_lockB)) 24 t1.start() 25 t2.start() 遞歸鎖:最常用的線程鎖,解決了死鎖現(xiàn)象 Rlock 首先本身就是個(gè)互斥鎖,維護(hù)了一個(gè)計(jì)數(shù)器,每次acquire就+1,release就-1,當(dāng)計(jì)數(shù)器為0的時(shí)候,大家才能搶這個(gè)鎖 1 import time 2 from threading import Thread,RLock 3 def f1(lokA,lokB): 4 lokA.acquire() 5 print('f1搶到了A鎖') 6 time.sleep(1) 7 lokB.acquire() 8 print('f1搶到B鎖了') 9 lokB.release() 10 lokA.release() 11 def f2(lokA,lokB): 12 lokB.acquire() 13 print('f2搶到了B鎖') 14 time.sleep(1) 15 lokA.acquire() 16 print('f2搶到了A鎖') 17 lokA.release() 18 lokB.release() 19 if __name__ == '__main__': 20 lokA = lokB = RLock() #遞歸鎖,維護(hù)一個(gè)計(jì)數(shù)器,acquire一次就加1,release就減1 21 t1 = Thread(target=f1,args=(lokA,lokB)) 22 t2 = Thread(target=f2,args=(lokA,lokB)) 23 t1.start() 24 t2.start() 多線程守護(hù)跟多進(jìn)程守護(hù)的區(qū)別: 區(qū)別:
守護(hù)進(jìn)程:主進(jìn)程代碼執(zhí)行運(yùn)行結(jié)束,守護(hù)進(jìn)程隨之結(jié)束
守護(hù)線程:守護(hù)線程會(huì)等待所有非守護(hù)線程運(yùn)行結(jié)束才結(jié)束 代碼示例: 1 import time 2 from threading import Thread 3 from multiprocessing import Process 4 def f1(): 5 time.sleep(2) 6 print('一號(hào)') 7 def f2(): 8 time.sleep(3) 9 print('二號(hào)') 10 if __name__ == '__main__': 11 t1 = Thread(target=f1,) 12 t2 = Thread(target=f2,) 13 t1.daemon = True 14 t2.daemon = True #守護(hù)線程:守護(hù)線程會(huì)等待所有非守護(hù)線程運(yùn)行結(jié)束才結(jié)束 15 t1.start() 16 t2.start() 17 print('線程結(jié)束') 18 p1 = Process(target=f1,) 19 p2 = Process(target=f2,) 20 p1.daemon = True 21 p2.daemon = True #守護(hù)進(jìn)程:主進(jìn)程代碼執(zhí)行運(yùn)行結(jié)束,守護(hù)進(jìn)程隨之結(jié)束 22 p1.start() 23 p2.start() 24 print('進(jìn)程結(jié)束') GIL鎖 : cpython解釋器上的一把互斥鎖
首先需要明確的一點(diǎn)是GIL并不是Python的特性,它是在實(shí)現(xiàn)Python解析器(CPython)時(shí)所引入的一個(gè)概念。就好比C++是一套語(yǔ)言(語(yǔ)法)標(biāo)準(zhǔn),但是可以用不同的編譯器來(lái)編譯成
可執(zhí)行代碼。有名的編譯器例如GCC,INTEL C++,Visual C++等。Python也一樣,同樣一段代碼可以通過(guò)CPython,PyPy,Psyco等不同的Python執(zhí)行環(huán)境來(lái)執(zhí)行。
像其中的JPython就沒(méi)有GIL。然而因?yàn)镃Python是大部分環(huán)境下默認(rèn)的Python執(zhí)行環(huán)境。所以在很多人的概念里CPython就是Python,也就想當(dāng)然的把GIL歸結(jié)為Python語(yǔ)言的缺陷。
所以這里要先明確一點(diǎn):GIL并不是Python的特性,Python完全可以不依賴于GIL
GIL本質(zhì)就是一把互斥鎖,既然是互斥鎖,所有互斥鎖的本質(zhì)都一樣,都是將并發(fā)運(yùn)行變成串行,以此來(lái)控制同一時(shí)間內(nèi)共享數(shù)據(jù)只能被一個(gè)任務(wù)所修改,進(jìn)而保證數(shù)據(jù)安全。
可以肯定的一點(diǎn)是:保護(hù)不同的數(shù)據(jù)的安全,就應(yīng)該加不同的鎖。
要想了解GIL,首先確定一點(diǎn):每次執(zhí)行python程序,都會(huì)產(chǎn)生一個(gè)獨(dú)立的進(jìn)程。例如python test.py,python aaa.py,python bbb.py會(huì)產(chǎn)生3個(gè)不同的python進(jìn)程
1 ''' 2 #驗(yàn)證python test.py只會(huì)產(chǎn)生一個(gè)進(jìn)程 3 #test.py內(nèi)容 4 import os,time 5 print(os.getpid()) 6 time.sleep(1000) 7 ''' 8 python3 test.py 9 #在windows下 10 tasklist |findstr python 11 #在linux下 12 ps aux |grep python?
如果多個(gè)線程的target=work,那么執(zhí)行流程是
多個(gè)線程先訪問(wèn)到解釋器的代碼,即拿到執(zhí)行權(quán)限,然后將target的代碼交給解釋器的代碼去執(zhí)行
解釋器的代碼是所有線程共享的,所以垃圾回收線程也可能訪問(wèn)到解釋器的代碼而去執(zhí)行,這就導(dǎo)致了一個(gè)問(wèn)題:對(duì)于同一個(gè)數(shù)據(jù)100,可能線程1執(zhí)行x=100的同時(shí),而垃圾回收?qǐng)?zhí)行的是回收100的操作,解決這種問(wèn)題沒(méi)有什么高明的方法,就是加鎖處理,如下圖的GIL,保證python解釋器同一時(shí)間只能執(zhí)行一個(gè)任務(wù)的代碼
?
?
GIL保護(hù)的是解釋器級(jí)的數(shù)據(jù),保護(hù)用戶自己的數(shù)據(jù)則需要自己加鎖處理,如下圖
?
結(jié)論:
對(duì)計(jì)算來(lái)說(shuō),cpu越多越好,但是對(duì)于I/O來(lái)說(shuō),再多的cpu也沒(méi)用
當(dāng)然對(duì)運(yùn)行一個(gè)程序來(lái)說(shuō),隨著cpu的增多執(zhí)行效率肯定會(huì)有所提高(不管提高幅度多大,總會(huì)有所提高),這是因?yàn)橐粋€(gè)程序基本上不會(huì)是純計(jì)算或者純I/O,所以我們只能相對(duì)的去看一個(gè)程序到底是計(jì)算密集型還是I/O密集型,從而進(jìn)一步分析python的多線程到底有無(wú)用武之地
#分析: 我們有四個(gè)任務(wù)需要處理,處理方式肯定是要玩出并發(fā)的效果,解決方案可以是: 方案一:開(kāi)啟四個(gè)進(jìn)程 方案二:一個(gè)進(jìn)程下,開(kāi)啟四個(gè)線程#單核情況下,分析結(jié)果: 如果四個(gè)任務(wù)是計(jì)算密集型,沒(méi)有多核來(lái)并行計(jì)算,方案一徒增了創(chuàng)建進(jìn)程的開(kāi)銷,方案二勝如果四個(gè)任務(wù)是I/O密集型,方案一創(chuàng)建進(jìn)程的開(kāi)銷大,且進(jìn)程的切換速度遠(yuǎn)不如線程,方案二勝#多核情況下,分析結(jié)果: 如果四個(gè)任務(wù)是計(jì)算密集型,多核意味著并行計(jì)算,在python中一個(gè)進(jìn)程中同一時(shí)刻只有一個(gè)線程執(zhí)行用不上多核,方案一勝如果四個(gè)任務(wù)是I/O密集型,再多的核也解決不了I/O問(wèn)題,方案二勝#結(jié)論:現(xiàn)在的計(jì)算機(jī)基本上都是多核,python對(duì)于計(jì)算密集型的任務(wù)開(kāi)多線程的效率并不能帶來(lái)多大性能上的提升,甚至不如串行(沒(méi)有大量切換),但是,對(duì)于IO密集型的任務(wù)效率還是有顯著提升的。計(jì)算密集型:多進(jìn)程效率高
1 from multiprocessing import Process 2 from threading import Thread 3 import os,time 4 def work(): 5 res=0 6 for i in range(100000000): 7 res*=i 8 9 10 if __name__ == '__main__': 11 l=[] 12 print(os.cpu_count()) #本機(jī)為4核 13 start=time.time() 14 for i in range(4): 15 p=Process(target=work) #耗時(shí)5s多 16 p=Thread(target=work) #耗時(shí)18s多 17 l.append(p) 18 p.start() 19 for p in l: 20 p.join() 21 stop=time.time() 22 print('run time is %s' %(stop-start))I/O密集型:多線程效率高
1 from multiprocessing import Process 2 from threading import Thread 3 import threading 4 import os,time 5 def work(): 6 time.sleep(2) 7 print('===>') 8 9 if __name__ == '__main__': 10 l=[] 11 print(os.cpu_count()) #本機(jī)為4核 12 start=time.time() 13 for i in range(400): 14 # p=Process(target=work) #耗時(shí)12s多,大部分時(shí)間耗費(fèi)在創(chuàng)建進(jìn)程上 15 p=Thread(target=work) #耗時(shí)2s多 16 l.append(p) 17 p.start() 18 for p in l: 19 p.join() 20 stop=time.time() 21 print('run time is %s' %(stop-start))應(yīng)用:
多線程用于IO密集型,如socket,爬蟲,web
多進(jìn)程用于計(jì)算密集型,如金融分析
寫一個(gè)簡(jiǎn)易socket多人聊天:
?
1 import socket 2 from threading import Thread 3 class MySocket: 4 def __init__(self,server_addr): 5 self.server_addr = server_addr 6 self.socket = socket.socket() 7 def serve_forever(self): 8 self.socket.bind(self.server_addr) 9 self.socket.listen(5) 10 self.build_connect() 11 def build_connect(self): #建立連接 12 while 1: 13 conn,addr = self.socket.accept() 14 t = Thread(target=self.handle,args=(conn,)) 15 t.start() 16 def handle(self,conn): 17 while 1: 18 from_client_msg = conn.recv(1024).decode('utf-8') 19 print('來(lái)自客戶的消息:',from_client_msg) 20 to_client_msg = input('服務(wù)端說(shuō):') 21 conn.send(to_client_msg.encode('utf-8')) 22 if __name__ == '__main__': 23 ip_port = ('127.0.0.1',8013) 24 server = MySocket(ip_port) 25 server.serve_forever()?
1 import socket 2 client = socket.socket() 3 client.connect(('127.0.0.1',8013)) 4 while 1: 5 to_server_msg = input('客戶端說(shuō):') 6 client.send(to_server_msg.encode('utf-8')) 7 from_server_msg = client.recv(1024).decode('utf-8') 8 print('來(lái)自服務(wù)端的消息:',from_server_msg)?
轉(zhuǎn)載于:https://www.cnblogs.com/Godisgirl/p/10257681.html
總結(jié)
以上是生活随笔為你收集整理的线程的创建 验证线程之间共享数据 守护线程 线程进程效率对比 锁 死锁 递归锁...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 前方高能 | 容器化应用操作原来可以这样
- 下一篇: Jmeter使用入门