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