MultiProcess-MultiThread
聽到一些關(guān)于python多進(jìn)程與多線的例子,感覺比較經(jīng)典,把一些例子分享一下.
內(nèi)容如下:
? ? Process、Thread、GIL、Process fork、Multiprocessing、Queue、ProcessPool、Multiprocess-Multithread comparison
(1) Process : 程序的一次執(zhí)行(程序編寫完畢后代碼裝載入內(nèi)存,系統(tǒng)分配資源運(yùn)行)。每個(gè)進(jìn)程有自己的內(nèi)存空間、數(shù)據(jù)棧等,只能使用進(jìn) 程間通訊,而不能直接共享信息?
(2) Thread線程:所有線程運(yùn)行在同一個(gè)進(jìn)程中,共享相同的運(yùn)行環(huán)境。 每個(gè)獨(dú)立的線程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序的出口; 線程的運(yùn)行可以被搶占(中斷),或暫時(shí)被掛起 (睡眠),讓其他線程運(yùn)行(讓步);一個(gè)進(jìn)程中的各個(gè)線程間共享同一片數(shù)據(jù)空間?
(3) 全局解釋器鎖GIL?
? ? GIL全稱全局解釋器鎖Global Interpreter Lock,GIL并不是Python的特性,它是在實(shí)現(xiàn)Python解析器(CPython)時(shí) 所引入的一個(gè)概念。?
? ? GIL是一把全局排他鎖,同一時(shí)刻只有一個(gè)線程在運(yùn)行。 毫無疑問全局鎖的存在會對多線程的效率有不小影響。甚至就幾乎等于Python是個(gè)單線程的程序。?
? ? multiprocessing庫的出現(xiàn)很大程度上是為了彌補(bǔ)thread庫因?yàn)?/span> GIL而低效的缺陷。它完整的復(fù)制了一套thread所提供的接口方便遷移。唯一的不同就是它使用了多進(jìn)程而不是多線程。每個(gè)進(jìn)程有自己的獨(dú)立的GIL,因此也不會出現(xiàn)進(jìn)程之間的GIL爭搶。?
多線程處理的例子:
from?threading?import?Thread import?time def?my_counter():i?=?0for?_?in?range(100000000):i?=i+1return?True def?main():thread_array?=?{}start_time?=?time.time()for?tid?in?range(2):t?=?Thread(target=my_counter())t.start()t.join()#以單線程、阻塞的方式順序運(yùn)行兩次my_counter函數(shù)end_time?=?time.time()print("Total?time:{}").format(end_time?-?start_time)if?__name__=="__main__":main()執(zhí)行結(jié)果如下:
Total time:12.7875118256
執(zhí)行結(jié)果如下:
Total time:15.8216409683
上述兩個(gè)例子的結(jié)果發(fā)現(xiàn):單線程運(yùn)行兩次函數(shù)的時(shí)間 ? 要比 ? 兩個(gè)線程同時(shí)運(yùn)行函數(shù)的時(shí)間短(僅限于本例)。
(4) fork操作:?
調(diào)用一次,返回兩次。因?yàn)椴僮飨到y(tǒng)自動把當(dāng)前進(jìn)程(稱為父 進(jìn)程)復(fù)制了一份(稱為子進(jìn)程),然后分別在父進(jìn)程和子進(jìn) 程內(nèi)返回。子進(jìn)程永遠(yuǎn)返回0,而父進(jìn)程返回子進(jìn)程的ID。子 進(jìn)程只需要調(diào)用getppid()就可以拿到父進(jìn)程的ID。
例:
執(zhí)行結(jié)果如下:
Process (16480) start ...
I (16480) just created a child process (16481).
I am child process (16481) and my parent is (16480)
(5) multiprocessing是跨平臺版本的多進(jìn)程模塊,它提供了一個(gè)Process類來代表一個(gè)進(jìn)程對象,下面是示例代碼:?
from?multiprocessing?import?Process import?time def?f(n):time.sleep(1)print?n*nif?__name__=="__main__":for?i?in?range(10):p=Process(target=f,args=[i,])p.start()#使用多進(jìn)程并發(fā)的方式執(zhí)行函數(shù)f這個(gè)程序如果用單進(jìn)程寫則需要執(zhí)行10秒以上的時(shí)間, 而用多進(jìn)程則啟動10個(gè)進(jìn)程并行執(zhí)行,只需要用1秒多的時(shí)間。多進(jìn)程時(shí),每個(gè)進(jìn)程各自有各自的GIL。而同一進(jìn)程中的多線程受到GIL的影響,效率返而會下降。
(6) Queue是多進(jìn)程安全的隊(duì)列,可以使用Queue實(shí)現(xiàn)多進(jìn)程之間的數(shù)據(jù)傳遞?
from?multiprocessing?import?Process,Queue import?time def?write(q):for?i?in?['A','B','C','D','E']:print?('Put?%s?to?queue'?%?i)q.put(i)time.sleep(0.5)def?read(q):while?True:v?=?q.get(True)print('get?%s?from?queue'?%v)if?__name__?==?'__main__':q?=?Queue()pw?=?Process(target=write,args=(q,))pr?=?Process(target=read,args=(q,))pw.start()pr.start()pr.join()pr.terminate()輸出結(jié)果:
Put A to queue
get A from queue
Put B to queue
get B from queue
Put C to queue
get C from queue
Put D to queue
get D from queue
Put E to queue
get E from queue
(7) 進(jìn)程池pool , 用于批量創(chuàng)建子進(jìn)程,可以靈活控制子進(jìn)程的數(shù)量?
from?multiprocessing?import?Pool import?time def?f(x):print?x*xtime.sleep(2)return?x*x if?__name__?==?'__main__':pool?=?Pool(processes=5)res_list?=?[]for?i?in?range(10):????????res?=?pool.apply_async(f,[i,])'''?以異步并行的方式啟動進(jìn)程處理函數(shù)f,如果要同步等待的方式,可以在每次進(jìn)程啟動之后調(diào)用res.get()方法,也可以使用Pool.apply'''print('-------:',i)res_list.append(res)pool.close()pool.join()for?r?in?res_list:print?'result',(r.get(timeout=5))輸出結(jié)果如下:
('-------:', 0)
('-------:', 1)
('-------:', 2)
('-------:', 3)
('-------:', 4)
('0-------:', 5)
('-------:', 6)
('-------:', 7)
1
('-------:', 8)
('-------:', 9)
4
16
9
25
36
49
64
81
result 0
result 1
result 4
result 9
result 16
result 25
result 36
result 49
result 64
result 81
如果使用同步方式,處理函數(shù)時(shí) 必須等待 前一個(gè)處理的結(jié)束,所以如果將該程序換為同步方式,輸出結(jié)果則是順序的。
(8) 多進(jìn)程與多線程的對比:
from?multiprocessing?import?Process import?threading import?time lock?=?threading.Lock() def?run(info_list,n):lock.acquire()info_list.append(n)lock.release()print('%s'?%?info_list) if?__name__?==?'__main__':info?=?[]for?i?in?range(10):p?=?Process(target=run,args=[info,i])p.start()p.join()time.sleep(1)print('-----------------threading--------------')for?i?in?range(10):p?=?threading.Thread(target=run,args=[info,i])p.start()p.join()輸出結(jié)果為:
[0]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
-----------------threading--------------
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
多進(jìn)程間數(shù)據(jù)不能直接共享,每次處理函數(shù)run的結(jié)果都不能繼承。而多線程間數(shù)據(jù)可以共享,但受到GIL的影響,run函數(shù)中在將數(shù)據(jù)追加到列表時(shí),使用lock鎖,追回完畢再釋放lock,這樣避免沖突。
轉(zhuǎn)載于:https://blog.51cto.com/caiyuanji/1966279
總結(jié)
以上是生活随笔為你收集整理的MultiProcess-MultiThread的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Postman完成接口测试
- 下一篇: Hyperledger Fabric 1