1.Process
創建進程的類:Process([group[,target[,name[,args[,kwargs]]]]]),target表示調用對象,args表示調用對象的位置參數元組。kwargs表示調用對象的字典。name為別名。group實質上不使用。
方法:is_alive()、join([timeout])、run()、start()、terminate()。其中,Process以start()啟動某個進程。
屬性:authkey、daemon(要通過start()設置)、exitcode(進程在運行時為None、如果為–N,表示被信號N結束)、name、pid。其中daemon是父進程終止後自動終止,且自己不能產生新進程,必須在start()之前設置。
例1.1:創建函數並將其作為單個進程
importmultiprocessing
importtime
defworker(interval):
n=5
whilen>0:
print("Thetimeis{0}".format(time.ctime()))
time.sleep(interval)
n-=1
if__name__=="__main__":
p=multiprocessing.Process(target=worker,args=(3,))
p.start()
print"p.pid:",p.pid
print"p.name:",p.name
print"p.is_alive:",p.is_alive()
結果
12345678p.pid:8736p.name:Process-1p.is_alive:TrueThetimeisTueApr2120:55:122015ThetimeisTueApr2120:55:152015ThetimeisTueApr2120:55:182015ThetimeisTueApr2120:55:212015ThetimeisTueApr2120:55:242015
例1.2:創建函數並將其作為多個進程
importmultiprocessing
importtime
defworker_1(interval):
print"worker_1"
time.sleep(interval)
print"endworker_1"
defworker_2(interval):
print"worker_2"
time.sleep(interval)
print"endworker_2"
defworker_3(interval):
print"worker_3"
time.sleep(interval)
print"endworker_3"
if__name__=="__main__":
p1=multiprocessing.Process(target=worker_1,args=(2,))
p2=multiprocessing.Process(target=worker_2,args=(3,))
p3=multiprocessing.Process(target=worker_3,args=(4,))
p1.start()
p2.start()
p3.start()
print("ThenumberofCPUis:"+str(multiprocessing.cpu_count()))
forpinmultiprocessing.active_children():
print("childp.name:"+p.name+" p.id"+str(p.pid))
print"END!!!!!!!!!!!!!!!!!"
結果
1234567891011ThenumberofCPUis:4childp.name:Process-3p.id7992childp.name:Process-2p.id4204childp.name:Process-1p.id6380END!!!!!!!!!!!!!!!!!worker_1worker_3worker_2endworker_1endworker_2endworker_3
例1.3:將進程定義為類
importmultiprocessing
importtime
classClockProcess(multiprocessing.Process):
def__init__(self,interval):
multiprocessing.Process.__init__(self)
self.interval=interval
defrun(self):
n=5
whilen>0:
print("thetimeis{0}".format(time.ctime()))
time.sleep(self.interval)
n-=1
if__name__=='__main__':
p=ClockProcess(3)
p.start()
註:進程p調用start()時,自動調用run()
結果
12345thetimeisTueApr2120:31:302015thetimeisTueApr2120:31:332015thetimeisTueApr2120:31:362015thetimeisTueApr2120:31:392015thetimeisTueApr2120:31:422015
2. python 多進程
基於官方文檔:
https://docs.python.org/zh-cn/3/library/multiprocessing.html
日樂購,剛才看到的一個博客,寫的都不太對,還是基於官方的比較穩妥
我就是喜歡抄官方的,哈哈
通常我們使用Process實例化一個進程,並調用 他的 start() 方法啟動它。
這種方法和 Thread 是一樣的。
上圖中,我寫了 p.join() 所以主進程是 等待 子進程執行完後,才執行 print("運行結束")
否則就是反過來了(這個不一定,看你的語句了,順序其實是隨機的)例如:
主進加個 sleep
所以不加join() ,其實子進程和主進程是各干各的,誰也不等誰。都執行完後,文件運行就結束了
上面我們用了 os.getpid() 和 os.getppid() 獲取 當前進程,和父進程的id
下面就講一下,這兩個函數的用法:
os.getpid()
返回當前進程的id
os.getppid()
返回父進程的id。 父進程退出後,unix 返回初始化進程(1)中的一個
windows返回相同的id (可能被其他進程使用了)
這也就解釋了,為啥我上面 的程序運行多次, 第一次列印的parentid 都是 14212 了。
而子進程的父級 process id 是調用他的那個進程的 id : 1940
視頻筆記:
多進程:使用大致方法:
參考: 進程通信(pipe和queue)
pool.map (函數可以有return 也可以共享內存或queue) 結果直接是個列表
poll.apply_async() (同map,只不過是一個進程,返回結果用 xx.get() 獲得)
報錯:
參考 : https://blog.csdn.net/xiemanR/article/details/71700531
把 pool = Pool() 放到 if name == " main ": 下面初始化搞定。
結果:
這個肯定有解釋的
測試多進程計算效果:
進程池運行:
結果:
普通計算:
我們同樣傳入 1 2 10 三個參數測試:
其實對比下來開始快了一半的;
我們把循環里的數字去掉一個 0;
單進程:
多進程:
兩次測試 單進程/進程池 分別為 0.669 和 0.772 幾乎成正比的。
問題 二:
視圖:
post 視圖裡面
Music 類:
直接報錯:
寫在 類裡面也 在函數里用 self.pool 調用也不行,也是相同的錯誤。
最後 把 pool = Pool 直接寫在 search 函數裡面,奇跡出現了:
前台也能顯示搜索的音樂結果了
總結一點,進程這個東西,最好 寫在 直接運行的函數裡面,而不是 一個函數跳來跳去。因為最後可能 是在子進程的子進程運行的,這是不許的,會報錯。
還有一點,多進程運行的函數對象,不能是 lambda 函數。也許lambda 虛擬,在內存??
使用 pool.map 子進程 函數報錯,導致整個 pool 掛了:
參考: https://blog.csdn.net/hedongho/article/details/79139606
主要你要,對函數內部捕獲錯誤,而不能讓異常拋出就可以了。
關於map 傳多個函數參數
我一開始,就是正常思維,多個參數,搞個元祖,讓參數一一對應不就行了:
報錯:
參考:
https://blog.csdn.net/qq_15969343/article/details/84672527
普通的 process 當讓可以穿多個參數,map 卻不知道咋傳的。
apply_async 和map 一樣,不知道咋傳的。
最簡單的方法:
使用 starmap 而不是 map
結果:
子進程結束
1.8399453163146973
成功拿到結果了
關於map 和 starmap 不同的地方看源碼:
關於apply_async() ,我沒找到多參數的方法,大不了用 一個迭代的 starmap 實現。哈哈
關於 上面源碼裡面有 itertools.starmap
itertools 用法參考:
https://docs.python.org/zh-cn/3/library/itertools.html#itertool-functions
有個問題,多進程最好不要使用全部的 cpu , 因為這樣可能影響其他任務,所以 在進程池 添加 process 參數 指定,cpu 個數:
上面就是預留了 一個cpu 干其他事的
後面直接使用 Queue 遇到這個問題:
解決:
Manager().Queue() 代替 Queue()
因為 queue.get() 是堵塞型的,所以可以提前判斷是不是 空的,以免堵塞進程。比如下面這樣:
使用 queue.empty() 空為True
3. 為什麼在Python里推薦使用多進程而不是多
最近在看Python的多線程,經常我們會聽到老手說:「Python下多線程是雞肋,推薦使用多進程!」,但是為什麼這么說呢?
要知其然,更要知其所以然。所以有了下面的深入研究:
首先強調背景:
1、GIL是什麼?
GIL的全稱是Global Interpreter Lock(全局解釋器鎖),來源是python設計之初的考慮,為了數據安全所做的決定。
2、每個CPU在同一時間只能執行一個線程(在單核CPU下的多線程其實都只是並發,不是並行,並發和並行從宏觀上來講都是同時處理多路請求的概念。但並發和並行又有區別,並行是指兩個或者多個事件在同一時刻發生;而並發是指兩個或多個事件在同一時間間隔內發生。)
在Python多線程下,每個線程的執行方式:
1、獲取GIL
2、執行代碼直到sleep或者是python虛擬機將其掛起。
3、釋放GIL
可見,某個線程想要執行,必須先拿到GIL,我們可以把GIL看作是「通行證」,並且在一個python進程中,GIL只有一個。拿不到通行證的線程,就不允許進入CPU執行。
在Python2.x里,GIL的釋放邏輯是當前線程遇見IO操作或者ticks計數達到100(ticks可以看作是Python自身的一個計數器,專門做用於GIL,每次釋放後歸零,這個計數可以通過 sys.setcheckinterval 來調整),進行釋放。
而每次釋放GIL鎖,線程進行鎖競爭、切換線程,會消耗資源。並且由於GIL鎖存在,python里一個進程永遠只能同時執行一個線程(拿到GIL的線程才能執行),這就是為什麼在多核CPU上,python的多線程效率並不高。
那麼是不是python的多線程就完全沒用了呢?
在這里我們進行分類討論:
1、CPU密集型代碼(各種循環處理、計數等等),在這種情況下,由於計算工作多,ticks計數很快就會達到閾值,然後觸發GIL的釋放與再競爭(多個線程來回切換當然是需要消耗資源的),所以python下的多
4. python中多進程+協程的使用以及為什麼要用它
前面講了為什麼python里推薦用多進程而不是多線程,但是多進程也有其自己的限制:相比線程更加笨重、切換耗時更長,並且在python的多進程下,進程數量不推薦超過CPU核心數(一個進程只有一個GIL,所以一個進程只能跑滿一個CPU),因為一個進程佔用一個CPU時能充分利用機器的性能,但是進程多了就會出現頻繁的進程切換,反而得不償失。
不過特殊情況(特指IO密集型任務)下,多線程是比多進程好用的。
舉個例子:給你200W條url,需要你把每個url對應的頁面抓取保存起來,這種時候,單單使用多進程,效果肯定是很差的。為什麼呢?
例如每次請求的等待時間是2秒,那麼如下(忽略cpu計算時間):
1、單進程+單線程:需要2秒*200W=400W秒==1111.11個小時==46.3天,這個速度明顯是不能接受的
2、單進程+多線程:例如我們在這個進程中開了10個多線程,比1中能夠提升10倍速度,也就是大約4.63天能夠完成200W條抓取,請注意,這里的實際執行是:線程1遇見了阻塞,CPU切換到線程2去執行,遇見阻塞又切換到線程3等等,10個線程都阻塞後,這個進程就阻塞了,而直到某個線程阻塞完成後,這個進程才能繼續執行,所以速度上提升大約能到10倍(這里忽略了線程切換帶來的開銷,實際上的提升應該是不能達到10倍的),但是需要考慮的是線程的切換也是有開銷的,所以不能無限的啟動多線程(開200W個線程肯定是不靠譜的)
3、多進程+多線程:這里就厲害了,一般來說也有很多人用這個方法,多進程下,每個進程都能佔一個cpu,而多線程從一定程度上繞過了阻塞的等待,所以比單進程下的多線程又更好使了,例如我們開10個進程,每個進程里開20W個線程,執行的速度理論上是比單進程開200W個線程快10倍以上的(為什麼是10倍以上而不是10倍,主要是cpu切換200W個線程的消耗肯定比切換20W個進程大得多,考慮到這部分開銷,所以是10倍以上)。
還有更好的方法嗎?答案是肯定的,它就是:
4、協程,使用它之前我們先講講what/why/how(它是什麼/為什麼用它/怎麼使用它)
what:
協程是一種用戶級的輕量級線程。協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此:
協程能保留上一次調用時的狀態(即所有局部狀態的一個特定組合),每次過程重入時,就相當於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。
在並發編程中,協程與線程類似,每個協程表示一個執行單元,有自己的本地數據,與其它協程共享全局數據和其它資源。
why:
目前主流語言基本上都選擇了多線程作為並發設施,與線程相關的概念是搶占式多任務(Preemptive multitasking),而與協程相關的是協作式多任務。
不管是進程還是線程,每次阻塞、切換都需要陷入系統調用(system call),先讓CPU跑操作系統的調度程序,然後再由調度程序決定該跑哪一個進程(線程)。
而且由於搶占式調度執行順序無法確定的特點,使用線程時需要非常小心地處理同步問題,而協程完全不存在這個問題(事件驅動和非同步程序也有同樣的優點)。
因為協程是用戶自己來編寫調度邏輯的,對CPU來說,協程其實是單線程,所以CPU不用去考慮怎麼調度、切換上下文,這就省去了CPU的切換開銷,所以協程在一定程度上又好於多線程。
how:
python裡面怎麼使用協程?答案是使用gevent,使用方法:看這里
使用協程,可以不受線程開銷的限制,我嘗試過一次把20W條url放在單進程的協程里執行,完全沒問題。
所以最推薦的方法,是多進程+協程(可以看作是每個進程里都是單線程,而這個單線程是協程化的)
多進程+協程下,避開了CPU切換的開銷,又能把多個CPU充分利用起來,這種方式對於數據量較大的爬蟲還有文件讀寫之類的效率提升是巨大的。
小例子:
[python]view plain
#-*-coding=utf-8-*-
importrequests
importgevent
fromgeventimportmonkey;monkey.patch_all()
importsys
reload(sys)
sys.setdefaultencoding('utf8')
deffetch(url):
try:
s=requests.Session()
r=s.get(url,timeout=1)#在這里抓取頁面
exceptException,e:
printe
return''
defprocess_start(url_list):
tasks=[]
forurlinurl_list:
tasks.append(gevent.spawn(fetch,url))
gevent.joinall(tasks)#使用協程來執行
deftask_start(filepath,flag=100000):#每10W條url啟動一個進程
withopen(filepath,'r')asreader:#從給定的文件中讀取url
url=reader.readline().strip()
url_list=[]#這個list用於存放協程任務
i=0#計數器,記錄添加了多少個url到協程隊列
whileurl!='':
i+=1
url_list.append(url)#每次讀取出url,將url添加到隊列
ifi==flag:#一定數量的url就啟動一個進程並執行
p=Process(target=process_start,args=(url_list,))
p.start()
url_list=[]#重置url隊列
i=0#重置計數器
url=reader.readline().strip()
ifurl_listnot[]:#若退出循環後任務隊列里還有url剩餘
p=Process(target=process_start,args=(url_list,))#把剩餘的url全都放到最後這個進程來執行
p.start()
if__name__=='__main__':
task_start('./testData.txt')#讀取指定文件
細心的同學會發現:上面的例子中隱藏了一個問題:進程的數量會隨著url數量的增加而不斷增加,我們在這里不使用進程池multiprocessing.Pool來控制進程數量的原因是multiprocessing.Pool和gevent有沖突不能同時使用,但是有興趣的同學可以研究一下gevent.pool這個協程池。
5. 關於python多進程使用(Queue、生產者和消費者)
關於 的生產者和消費者的實現,剛好最近有用到,簡單總結記錄下:
是系統獨立調度核分配系統資源(CPU、內存)的基本單位,進程之間是相互獨立的,每啟動一個新的進程相當於把數據進行了一次克隆。
python提供了多種方法實現了多進程中間的 (可以修改同一份數據)。
GIL 的全稱是 Global Interpreter Lock(全局解釋器鎖),來源是 Python 設計之初的考慮,為了數據安全所做的決定。
某個線程想要執行,必須先拿到 GIL,我們可以把 GIL 看作是「通行證」,並且在一個 Python 進程中,GIL 只有一個,這就導致了多線程搶佔GIL耗時。這就是為什麼在多核CPU上,Python 的多線程效率並不高的根本原因。
所以有必要學習下多進程的使用。
6. python循環怎麼用多線程去運行
背景:Python腳本:讀取文件中每行,放入列表中;循環讀取列表中的每個元素,並做處理操作。
核心:多線程處理單個for循環函數調用
模塊:threading
第一部分:
:多線程腳本 (該腳本只有兩個線程,t1循環次數<t2)#!/usr/bin/env python#-*- coding: utf8 -*- import sysimport timeimport stringimport threadingimport datetimefileinfo = sys.argv[1] # 讀取文件內容放入列表host_list = []port_list = [] # 定義函數:讀取文件內容放入列表中def CreateList(): f = file(fileinfo,'r') for line in f.readlines(): host_list.append(line.split(' ')[0]) port_list.append(line.split(' ')[1]) return host_list return port_list f.close() # 單線程 循環函數,注釋掉了#def CreateInfo(): # for i in range(0,len(host_list)): # 單線程:直接循環列表# time.sleep(1)# TimeMark = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')# print "The Server's HostName is %-15s and Port is %-4d !!! [%s]" % (host_list[i],int(port_list[i]),TimeMark)# # 定義多線程循環調用函數def MainRange(start,stop): #提供列表index起始位置參數 for i in range(start,stop): time.sleep(1) TimeMark = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print "The Server's HostName is %-15s and Port is %-4d !!! [%s]" % (host_list[i],int(port_list[i]),TimeMark) # 執行函數,生成列表CreateList()# 列表分割成:兩部分 mid為列表的index中間位置mid = int(len(host_list)/2) # 多線程部分threads = []t1 = threading.Thread(target=MainRange,args=(0,mid))threads.append(t1)t2 = threading.Thread(target=MainRange,args=(mid,len(host_list)))threads.append(t2) for t in threads: t.setDaemon(True) t.start()t.join()print "ok"
以上是腳本內容!!!
----------------------------------------------------------------------
:讀取文件的內容
文件內容:
[root@monitor2 logdb]# cat hostinfo.txt
192.168.10.11 1011
192.168.10.12 1012
192.168.10.13 1013
192.168.10.14 1014
192.168.10.15 1015
192.168.10.16 1016
192.168.10.17 1017
192.168.10.18 1018
192.168.10.19 1019
192.168.10.20 1020
192.168.10.21 1021
192.168.10.22 1022
192.168.10.23 1023
192.168.10.24 1024
192.168.10.25 1025
:輸出結果:
單線程 : 執行腳本:輸出結果:
[root@monitor2 logdb]# ./Threadfor.py hostinfo.txt
The Server's HostName is 192.168.10.10 and Port is 1010 !!! [2017-01-10 14:25:14]
The Server's HostName is 192.168.10.11 and Port is 1011 !!! [2017-01-10 14:25:15]
The Server's HostName is 192.168.10.12 and Port is 1012 !!! [2017-01-10 14:25:16]
.
.
.
The Server's HostName is 192.168.10.25 and Port is 1025 !!! [2017-01-10 14:25:29]
多線程:執行腳本:輸出 結果
[root@monitor2 logdb]# ./Threadfor.py hostinfo.txt
The Server's HostName is 192.168.10.11 and Port is 1011 !!! [2017-01-10 14:51:51]
The Server's HostName is 192.168.10.18 and Port is 1018 !!! [2017-01-10 14:51:51]
The Server's HostName is 192.168.10.12 and Port is 1012 !!! [2017-01-10 14:51:52]
The Server's HostName is 192.168.10.19 and Port is 1019 !!! [2017-01-10 14:51:52]
The Server's HostName is 192.168.10.13 and Port is 1013 !!! [2017-01-10 14:51:53]
The Server's HostName is 192.168.10.20 and Port is 1020 !!! [2017-01-10 14:51:53]
The Server's HostName is 192.168.10.14 and Port is 1014 !!! [2017-01-10 14:51:54]
The Server's HostName is 192.168.10.21 and Port is 1021 !!! [2017-01-10 14:51:54]
The Server's HostName is 192.168.10.15 and Port is 1015 !!! [2017-01-10 14:51:55]
The Server's HostName is 192.168.10.22 and Port is 1022 !!! [2017-01-10 14:51:55]
The Server's HostName is 192.168.10.16 and Port is 1016 !!! [2017-01-10 14:51:56]
The Server's HostName is 192.168.10.23 and Port is 1023 !!! [2017-01-10 14:51:56]
The Server's HostName is 192.168.10.17 and Port is 1017 !!! [2017-01-10 14:51:57]
The Server's HostName is 192.168.10.24 and Port is 1024 !!! [2017-01-10 14:51:57]
The Server's HostName is 192.168.10.25 and Port is 1025 !!! [2017-01-10 14:51:58]
7. 一個for循環的Python腳本程序中如何加入多進程(並發進程)呢,急急急,在線等
#下面是一個示例,我寫了一個簡單的for循環,並加入了多線程並發。
#-*-coding:utf-8-*-
importthread,threading
#TestFunction
defForTest():
foriinrange(10):
printi
classmythread(threading.Thread):
def__init__(self,threadname):
threading.Thread.__init__(self)
defrun(self):
lock.acquire()
forjinxrange(int(times)):
#AddOwnFuctionHere
ForTest()
lock.release()
defMutiThread(num,times):
threads=[]
globalft
forxinxrange(num):
threads.append(mythread(num))
fortinthreads:
lock.acquire()
t.start()
lock.release()
fortinthreads:
t.join()
if__name__=='__main__':
globalnum,times,lock
num=2#num並發數
times=2#times運行次數
lock=threading.Lock()
MutiThread(num,times)
運行結果:
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
8. Python多進程運行——Multiprocessing基礎教程2
上篇文章簡單介紹了multiprocessing模塊,本文將要介紹進程之間的數據共享和信息傳遞的概念。
在多進程處理中,所有新創建的進程都會有這兩個特點:獨立運行,有自己的內存空間。
我們來舉個例子展示一下:
這個程序的輸出結果是:
在上面的程序中我們嘗試在兩個地方列印全局列表result的內容:
我們再用一張圖來幫助理解記憶不同進程間的數據關系:
如果程序需要在不同的進程之間共享一些數據的話,該怎麼做呢?不用擔心,multiprocessing模塊提供了Array對象和Value對象,用來在進程之間共享數據。
所謂Array對象和Value對象分別是指從共享內存中分配的ctypes數組和對象。我們直接來看一個例子,展示如何用Array對象和Value對象在進程之間共享數據:
程序輸出的結果如下:
成功了!主程序和p1進程輸出了同樣的結果,說明程序中確實完成了不同進程間的數據共享。那麼我們來詳細看一下上面的程序做了什麼:
在主程序中我們首先創建了一個Array對象:
向這個對象輸入的第一個參數是數據類型:i表示整數,d代表浮點數。第二個參數是數組的大小,在這個例子中我們創建了包含4個元素的數組。
類似的,我們創建了一個Value對象:
我們只對Value對象輸入了一個參數,那就是數據類型,與上述的方法一致。當然,我們還可以對其指定一個初始值(比如10),就像這樣:
隨後,我們在創建進程對象時,將剛創建好的兩個對象:result和square_sum作為參數輸入給進程:
在函數中result元素通過索引進行數組賦值,square_sum通過 value 屬性進行賦值。
注意:為了完整列印result數組的結果,需要使用 result[:] 進行列印,而square_sum也需要使用 value 屬性進行列印:
每當python程序啟動時,同時也會啟動一個伺服器進程。隨後,只要我們需要生成一個新進程,父進程就會連接到伺服器並請求它派生一個新進程。這個伺服器進程可以保存Python對象,並允許其他進程使用代理來操作它們。
multiprocessing模塊提供了能夠控制伺服器進程的Manager類。所以,Manager類也提供了一種創建可以在不同流程之間共享的數據的方法。
伺服器進程管理器比使用共享內存對象更靈活,因為它們可以支持任意對象類型,如列表、字典、隊列、值、數組等。此外,單個管理器可以由網路上不同計算機上的進程共享。
但是,伺服器進程管理器的速度比使用共享內存要慢。
讓我們來看一個例子:
這個程序的輸出結果是:
我們來理解一下這個程序做了什麼:首先我們創建了一個manager對象
在with語句下的所有行,都是在manager對象的范圍內的。接下來我們使用這個manager對象創建了列表(類似的,我們還可以用 manager.dict() 創建字典)。
最後我們創建了進程p1(用於在records列表中插入一條新的record)和p2(將records列印出來),並將records作為參數進行傳遞。
伺服器進程的概念再次用下圖總結一下:
為了能使多個流程能夠正常工作,常常需要在它們之間進行一些通信,以便能夠劃分工作並匯總最後的結果。multiprocessing模塊支持進程之間的兩種通信通道:Queue和Pipe。
使用隊列來回處理多進程之間的通信是一種比較簡單的方法。任何Python對象都可以使用隊列進行傳遞。我們來看一個例子:
上面這個程序的輸出結果是:
我們來看一下上面這個程序到底做了什麼。首先我們創建了一個Queue對象:
然後,將這個空的Queue對象輸入square_list函數。該函數會將列表中的數平方,再使用 put() 方法放入隊列中:
隨後使用 get() 方法,將q列印出來,直至q重新稱為一個空的Queue對象:
我們還是用一張圖來幫助理解記憶:
一個Pipe對象只能有兩個端點。因此,當進程只需要雙向通信時,它會比Queue對象更好用。
multiprocessing模塊提供了 Pipe() 函數,該函數返回由管道連接的一對連接對象。 Pipe() 返回的兩個連接對象分別表示管道的兩端。每個連接對象都有 send() 和 recv() 方法。
我們來看一個例子:
上面這個程序的輸出結果是:
我們還是來看一下這個程序到底做了什麼。首先創建了一個Pipe對象:
與上文說的一樣,該對象返回了一對管道兩端的兩個連接對象。然後使用 send() 方法和 recv() 方法進行信息的傳遞。就這么簡單。在上面的程序中,我們從一端向另一端發送一串消息。在另一端,我們收到消息,並在收到END消息時退出。
要注意的是,如果兩個進程(或線程)同時嘗試從管道的同一端讀取或寫入管道中的數據,則管道中的數據可能會損壞。不過不同的進程同時使用管道的兩端是沒有問題的。還要注意,Queue對象在進程之間進行了適當的同步,但代價是增加了計算復雜度。因此,Queue對象對於線程和進程是相對安全的。
最後我們還是用一張圖來示意:
Python的multiprocessing模塊還剩最後一篇文章:多進程的同步與池化
敬請期待啦!