python多线程多进程多协程_python 多进程、多线程、协程
1、python的多線程
多線程就是在同一時(shí)刻執(zhí)行多個(gè)不同的程序,然而python中的多線程并不能真正的實(shí)現(xiàn)并行,這是由于cpython解釋器中的GIL(全局解釋器鎖)搗的鬼,這把鎖保證了同一時(shí)刻只有一個(gè)線程被執(zhí)行。
多線程的特點(diǎn):
線程比進(jìn)程更輕量級(jí),創(chuàng)建一個(gè)線程要比創(chuàng)建一個(gè)進(jìn)程快10-100倍。
線程共享全局變量。
由于GIL的原因,當(dāng)一個(gè)線程遇到IO操作時(shí),會(huì)切換到另一個(gè)線程,所以線程適合IO密集型操作。
在多核cpu系統(tǒng)中,最大限度的利用多核,可以開啟多個(gè)線程,開銷比進(jìn)程小的多,但是這并不適合python。
多線程互斥鎖:
因?yàn)榫€程共享全局變量,所以需要互斥鎖去限制線程對(duì)全局變量的更改。
假設(shè),當(dāng)一個(gè)線程在執(zhí)行到獲取全局變量的時(shí)候,這個(gè)后GIL切換到另一個(gè)線程執(zhí)行,這個(gè)時(shí)候新的線程為全局變量+1后切換回之前的線程,之前的線程中的全局變量還是+1前的值,所以需要互斥鎖。
為什么有了GIL鎖還要互斥鎖呢?
GIL鎖只是控制同一時(shí)刻下只有一個(gè)線程被執(zhí)行,這并不能控制同一時(shí)刻只有一個(gè)線程去獲取并更改全局變量,所以需要使用互斥鎖。
多線程的實(shí)現(xiàn):
#導(dǎo)入threading模塊
importthreading#定義全局變量
i=0#定義互斥鎖
mutex =threading.Lock()defa():#申明全局變量i
globalifor j in range(2000000):#獲取互斥鎖
mutex.acquire()
i+=1
#釋放互斥鎖
mutex.release()defb():globalifor j in range(2000000):
mutex.acquire()
i+=1mutex.release()#創(chuàng)建線程
t1 = threading.Thread(target=a)
t2= threading.Thread(target=b)#開啟線程
t1.start()
t2.start()#等待所有線程結(jié)束
t1.join()
t2.join()print(i)
2、python中的多進(jìn)程
python的多線程不能利用多核的優(yōu)勢(shì),如果想要充分的利用多核cpu的資源,python中大部分情況需要使用多進(jìn)程。
python多進(jìn)程的特點(diǎn):
進(jìn)程間不共享全局變量,進(jìn)程修改的數(shù)據(jù)僅限于該進(jìn)程內(nèi)。
進(jìn)程創(chuàng)建和銷毀的開銷比較大。
相對(duì)于線程,進(jìn)程更適合與計(jì)算密集型操作。
能充分利用多核的優(yōu)勢(shì)。
進(jìn)程間通信:
既然進(jìn)程間中不公共享全局變量,那么多進(jìn)程間怎么進(jìn)行通信呢?可以使用multiprocessing中的Queue模塊,當(dāng)然也可以使用socket、管道、共享內(nèi)存等方式。
多進(jìn)程的實(shí)現(xiàn):
#導(dǎo)入multiprocessin模塊
importmultiprocessing#創(chuàng)建隊(duì)列
queue =multiprocessing.Queue()#定義全局變量
a =0#定義函數(shù)
defwork1(num):#獲取隊(duì)列中的數(shù)據(jù),如果沒(méi)有數(shù)據(jù),將堵塞
a =queue.get()#將隊(duì)列中的數(shù)據(jù)+2000000次num
for i in range(2000000):
a+=num#將數(shù)據(jù)存放在隊(duì)列中
queue.put(a)#打印最終結(jié)果
print("work1",a)#定義函數(shù)
defwork2():#申明全局變量a
globala#將a+2000000次1
for i in range(2000000):
a+=1
#打印最總結(jié)果
print("work2",a)#將a存放在隊(duì)列中
queue.put(a)#創(chuàng)建進(jìn)程
p1 = multiprocessing.Process(target=work1, args=(2,))
p2= multiprocessing.Process(target=work2)#啟動(dòng)進(jìn)程
p1.start()
p2.start()#等待進(jìn)程結(jié)束
p1.join()
p2.join()#獲取隊(duì)列中的數(shù)據(jù)
a =queue.get()#打印a
print(a)
進(jìn)程池的實(shí)現(xiàn)
進(jìn)程池能減少重復(fù)創(chuàng)建和銷毀進(jìn)程的開銷問(wèn)題
#導(dǎo)入需要的模塊
importmultiprocessingimporttimeimportrandom#定義函數(shù)
defwork(num):print("num=",num)
time.sleep(random.randint(0,2))#創(chuàng)建進(jìn)程池,設(shè)置進(jìn)程的數(shù)量
pool = multiprocessing.Pool(3)for i in range(10):#開啟進(jìn)程
pool.apply_async(work, args=(i,))#設(shè)置等待時(shí)間,等待所有進(jìn)程結(jié)束
time.sleep(20)
3、python中的協(xié)程
在linux中線程就是輕量級(jí)的進(jìn)程,而我們通常也把協(xié)程稱為輕量級(jí)的線程。
對(duì)比進(jìn)程和協(xié)程:
進(jìn)程是內(nèi)核調(diào)度,而協(xié)程是在用戶態(tài)調(diào)度,所以說(shuō)進(jìn)程的上下文在內(nèi)核態(tài)保存恢復(fù),而協(xié)程是在用戶態(tài)保存恢復(fù)的,所以協(xié)程的開銷比進(jìn)程低。
進(jìn)程會(huì)被搶占,而協(xié)程不會(huì),也就是說(shuō)協(xié)程如果不主動(dòng)讓出cpu,那么其他的協(xié)程就沒(méi)有執(zhí)行的機(jī)會(huì)。
進(jìn)程所需要的內(nèi)存比協(xié)程大得多
對(duì)比線程和協(xié)程:
線程的上下文切換成本相對(duì)于協(xié)程來(lái)說(shuō)比較高。
線程的切換由操作系統(tǒng)來(lái)控制,而協(xié)程的切換由我們自己控制。
yield實(shí)現(xiàn)協(xié)程:
#定義兩個(gè)函數(shù)
defwork1():whileTrue:print("work1")#當(dāng)程序運(yùn)行到y(tǒng)ield就會(huì)暫停,等待下次的next調(diào)用,然后繼續(xù)執(zhí)行
yield
defwork2():whileTrue:print("work2")yieldw1=work1()
w2=work2()whileTrue:#使用next函數(shù)啟動(dòng)
next(w1)
next(w2)
greenlet實(shí)現(xiàn)協(xié)程:
greenlet安裝:
sudo pip3 install greenlet
code:
#導(dǎo)入greenlet模塊
from greenlet importgreenletdefwork1():for i in range(10):print("work1")#打印過(guò)后跳轉(zhuǎn)至協(xié)程g2繼續(xù)執(zhí)行
g2.switch()defwork2():for i in range(10):print("work2")#打印后跳轉(zhuǎn)至協(xié)程g1繼續(xù)執(zhí)行
g1.switch()#創(chuàng)建協(xié)程g1
g1 =greenlet(work1)#創(chuàng)建協(xié)程g2
g2 =greenlet(work2)#跳轉(zhuǎn)至協(xié)程g1
g1.switch()
gevent實(shí)現(xiàn)協(xié)程:
gevent是基于greenlet的并發(fā)網(wǎng)絡(luò)庫(kù),每當(dāng)有一個(gè)協(xié)程堵塞的時(shí),程序?qū)⒆詣?dòng)調(diào)度。
monkey-patching:
一般稱為猴子補(bǔ)丁,這個(gè)補(bǔ)丁能直接修改標(biāo)準(zhǔn)庫(kù)里面大部分的阻塞式系統(tǒng)調(diào)用。但是如果在復(fù)雜的生產(chǎn)環(huán)境中使用了這些標(biāo)準(zhǔn)庫(kù),可能就會(huì)因?yàn)榇蛄搜a(bǔ)丁而出現(xiàn)奇怪的問(wèn)題。
gevent安裝:
sudo pip3 install gevent
code:
#導(dǎo)入所需要的模塊
importgeventimporttimefrom gevent importmonkey#猴子補(bǔ)丁,monkey.patch_all()方法將所有的標(biāo)準(zhǔn)庫(kù)都替換掉#使用猴子補(bǔ)丁褒貶不一,但是官網(wǎng)上還是建議使用patch_all(),而且在程序的第一行就執(zhí)行
monkey.patch_all()deff(n):for i inrange(n):print(i)#設(shè)置延時(shí)
time.sleep(0.5)#如果沒(méi)有導(dǎo)入monkey模塊的話,需要使用gevent.sleep()
#gevent.sleep(0.5)
#----------------寫法一--------------------#創(chuàng)建greenlet協(xié)程對(duì)象#g1 = gevent.spawn(f,5)#g2 = gevent.spawn(f,5)#g3 = gevent.spawn(f,5)#等待所有g(shù)reenlet攜程結(jié)束后退出#g1.join()#g2.join()#g3.join()
#----------------寫法二--------------------
gevent.joinall([gevent.spawn(f,5), gevent.spawn(f,5), gevent.spawn(f,5)])
總結(jié)
以上是生活随笔為你收集整理的python多线程多进程多协程_python 多进程、多线程、协程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 不同域名cookie共享_cookie在
- 下一篇: python tkinter butto