源代碼 Lib/multiprocessing/
multiprocessing 是一個用與 threading 模塊相似API的支持產生進程的包。 multiprocessing 包同時提供本地和遠程併發,使用子進程代替線程,有效避免 Global Interpreter Lock 帶來的影響。因此, multiprocessing 模塊允許程序員充分利用機器上的多個核心。Unix 和 Windows 上都可以運行。
multiprocessing 模塊還引入了在 threading 模塊中沒有類似物的API。這方面的一個主要例子是 Pool 對象,它提供了一種方便的方法,可以跨多個輸入值並行化函數的執行,跨進程分配輸入數據(數據並行)。以下示例演示了在模塊中定義此類函數的常見做法,以便子進程可以成功導入該模塊。
Process 類
在 multiprocessing 中,通過創建一個 Process 對象然後調用它的 start() 方法來生成進程。 Process 和 threading.Thread API 相同。 一個簡單的多進程程序示例是:
from multiprocessing import Process def f(name): print('hello', name) if __name__ == '__main__': p = Process(target=f, args=('bob',)) p.start() p.join()
根據不同的平臺, multiprocessing 支持三種啓動進程的方法。這些 啓動方法 有
- spawn
父進程啓動一個新的Python解釋器進程。子進程只會繼承那些運行進程對象的 run() 方法所需的資源。特別是父進程中非必須的文件描述符和句柄不會被繼承。相對於使用 fork 或者 forkserver,使用這個方法啓動進程相當慢。
可在Unix和Windows上使用。 Windows上的默認設置。
- fork
父進程使用 os.fork() 來產生 Python 解釋器分叉。子進程在開始時實際上與父進程相同。父進程的所有資源都由子進程繼承。請注意,安全分叉多線程進程是棘手的。
只存在於Unix。Unix中的默認值。
- forkserver
程序啓動並選擇* forkserver * 啓動方法時,將啓動服務器進程。從那時起,每當需要一個新進程時,父進程就會連接到服務器並請求它分叉一個新進程。分叉服務器進程是單線程的,因此使用 os.fork() 是安全的。沒有不必要的資源被繼承。
可在Unix平臺上使用,支持通過Unix管道傳遞文件描述符。
在進程之間交換對象
multiprocessing 支持進程之間的兩種通信通道:
- 隊列
Queue 類是一個近似 queue.Queue 的克隆。 例如:
from multiprocessing import Process, Queue def f(q): q.put([42, None, 'hello']) if __name__ == '__main__': q = Queue() p = Process(target=f, args=(q,)) p.start() print(q.get()) # prints "[42, None, 'hello']" p.join()
隊列是線程和進程安全的。
- 管道
Pipe() 函數返回一個由管道連接的連接對象,默認情況下是雙工(雙向)。例如:
from multiprocessing import Process, Pipe def f(conn): conn.send([42, None, 'hello']) conn.close() if __name__ == '__main__': parent_conn, child_conn = Pipe() p = Process(target=f, args=(child_conn,)) p.start() print(parent_conn.recv()) # prints "[42, None, 'hello']" p.join()
返回的兩個連接對象 Pipe() 表示管道的兩端。每個連接對象都有 send() 和 recv() 方法(相互之間的)。請注意,如果兩個進程(或線程)同時嘗試讀取或寫入管道的 同一 端,則管道中的數據可能會損壞。當然,同時使用管道的不同端的進程不存在損壞的風險。
Process 和異常
class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
進程對象表示在單獨進程中運行的活動。 Process 類等價於 threading.Thread 。
應始終使用關鍵字參數調用構造函數。 group 應該始終是 None ;它僅用於兼容 threading.Thread 。 target 是由 run() 方法調用的可調用對象。它默認爲 None ,意味着什麼都沒有被調用。 name 是進程名稱(有關詳細信息,請參閱 name )。 args 是目標調用的參數元組。 kwargs 是目標調用的關鍵字參數字典。如果提供,則鍵參數 daemon 將進程 daemon 標誌設置爲 True 或 False 。如果是 None (默認值),則該標誌將從創建的進程繼承。
默認情況下,不會將任何參數傳遞給 target 。
如果子類重寫構造函數,它必須確保它在對進程執行任何其他操作之前調用基類構造函數( Process.__init__() )。
在 3.3 版更改: 加入 daemon 參數。
run()
表示進程活動的方法。
你可以在子類中重載此方法。標準 run() 方法調用傳遞給對象構造函數的可調用對象作爲目標參數(如果有),分別從 args 和 kwargs 參數中獲取順序和關鍵字參數。
start()
啓動進程活動。
每個進程對象最多隻能調用一次。它安排對象的 run() 方法在一個單獨的進程中調用。
join([timeout])
如果可選參數 timeout 是 None (默認值),則該方法將阻塞,直到調用 join() 方法的進程終止。如果 timeout 是一個正數,它最多會阻塞 timeout 秒。請注意,如果進程終止或方法超時,則該方法返回 None 。檢查進程的 exitcode 以確定它是否終止。
一個進程可以合併多次。
進程無法併入自身,因爲這會導致死鎖。嘗試在啓動進程之前合併進程是錯誤的。
name
進程的名稱。該名稱是一個字符串,僅用於識別目的。它沒有語義。可以爲多個進程指定相同的名稱。
初始名稱由構造器設定。 如果沒有爲構造器提供顯式名稱,則會構造一個形式爲 ‘Process-N1:N2:…:Nk’ 的名稱,其中每個 Nk 是其父親的第 N 個孩子。
is_alive()
返回進程是否還活着。
粗略地說,從 start() 方法返回到子進程終止之前,進程對象仍處於活動狀態。
daemon
進程的守護標誌,一個布爾值。這必須在 start() 被調用之前設置。
初始值繼承自創建進程。
當進程退出時,它會嘗試終止其所有守護進程子進程。
請注意,不允許守護進程創建子進程。否則,守護進程會在子進程退出時終止其子進程。 另外,這些 不是 Unix守護進程或服務,它們是正常進程,如果非守護進程已經退出,它們將被終止(並且不被合併)。
除了 threading.Thread API ,Process 對象還支持以下屬性和方法:
pid
返回進程ID。在生成該進程之前,這將是 None 。
exitcode
的退子進程出代碼。如果進程尚未終止,這將是 None 。負值 -N 表示孩子被信號 N 終止。
authkey
進程的身份驗證密鑰(字節字符串)。
當 multiprocessing 初始化時,主進程使用 os.urandom() 分配一個隨機字符串。
當創建 Process 對象時,它將繼承其父進程的身份驗證密鑰,儘管可以通過將 authkey 設置爲另一個字節字符串來更改。
參見 認證密碼 。
sentinel
系統對象的數字句柄,當進程結束時將變爲 “ready” 。
如果要使用 multiprocessing.connection.wait() 一次等待多個事件,可以使用此值。否則調用 join() 更簡單。
在Windows上,這是一個操作系統句柄,可以與 WaitForSingleObject 和 WaitForMultipleObjects 系列API調用一起使用。在Unix上,這是一個文件描述符,可以使用來自 select 模塊的原語。
3.3 新版功能.
terminate()
終止進程。 在Unix上,這是使用 SIGTERM 信號完成的;在Windows上使用 TerminateProcess() 。 請注意,不會執行退出處理程序和finally子句等。
請注意,進程的後代進程將不會被終止 —— 它們將簡單地變成孤立的。
警告 如果在關聯進程使用管道或隊列時使用此方法,則管道或隊列可能會損壞,並可能無法被其他進程使用。類似地,如果進程已獲得鎖或信號量等,則終止它可能導致其他進程死鎖。
注意 start() 、 join() 、 is_alive() 、 terminate() 和 exitcode 方法只能由創建進程對象的進程調用。
Process 一些方法的示例用法:
>>> import multiprocessing, time, signal >>> p = multiprocessing.Process(target=time.sleep, args=(1000,)) >>> print(p, p.is_alive()) <Process(Process-1, initial)> False >>> p.start() >>> print(p, p.is_alive()) <Process(Process-1, started)> True >>> p.terminate() >>> time.sleep(0.1) >>> print(p, p.is_alive()) <Process(Process-1, stopped[SIGTERM])> False >>> p.exitcode == -signal.SIGTERM True
exception multiprocessing.ProcessError
所有 multiprocessing 異常的基類。
exception multiprocessing.BufferTooShort
當提供的緩衝區對象太小而無法讀取消息時, Connection.recv_bytes_into() 引發的異常。
如果 e 是一個 BufferTooShort 實例,那麼 e.args[0] 將把消息作爲字節字符串給出。
exception multiprocessing.AuthenticationError
出現身份驗證錯誤時引發。
exception multiprocessing.TimeoutError
有超時的方法超時時引發。