關於python的三種子程序啟動方式:fork, spawn, forkserver

LCAC發表於2024-04-02

背景:

最近在學習d2l的時候,執行for X, y in train_iter:的時候,總是報錯。檢視報錯的內容:

RuntimeError: DataLoader worker (pid 41847) exited unexpectedly with exit code 1. 
Details are lost due to multiprocessing. Rerunning with num_workers=0 may give better error trace.

看到這個提示,想到了在呼叫d2l.load_data_fashion_mnist(batch_size)的時候,內部會設定4個程序進行預讀取資料,那麼問題可能就出在這裡。

問了gpu之後,給的提示是要在這個判斷裡面執行一系列的操作:if __name__ == "__main__":。因為是學習階段所以沒有管是不是主程序,都是直接全域性寫。可能是導致了該原因。

此時就很困惑,在預讀取資料建立多程序,為何會影響到另外這邊的程式碼的執行。

透過查詢資料之後,確實瞭解到了原來python有3種不同的建立子程序的方式,並且不同的平臺,預設的建立方式還可能不一樣。

如下是對各個不同程序的建立的說明

二、fork的啟動方式

1、這是Unix/linux系統上預設的啟動方式。當程序使用fork時,子程序會複製父程序的整個執行環境,跟咱們平時linux的C++執行的fork是一樣的。子程序通常立即開始執行,在某些情況下這可能會導致狀態衝突。

2、因為子程序會無條件地複製父程序的資源和狀態,某些資源可能不應該被兩個程序共享(例如,鎖、檔案描述符)。

三、spawn的啟動方式

1、這是windows系統上預設的啟動方式。啟動方法會建立一個全新的Python直譯器程序,並只將需要執行的函式及其引數序列化後傳送給子程序。與`fork`不同,父程序的資源並沒有被子程序複製。所有的準備工作都是在子程序中“從頭開始”的,這就意味著子程序基本上是一個全新的例項,而不是父程序的一個複製品。

2、spawn的建立方式避免了由於資源共享帶來的狀態衝突和錯誤。但是建立程序的速度相對會比較慢,因為還要建立一個全新的解釋程序

四、forkserver的啟動方式

1、這是Unix和類Unix系統上可用。(讀者沒有跑成功過,所以這裡只寫理論)

2、`forkserver`是一個特別的啟動方法,它對`fork`做了一些改良。當程式第一次初始化multiprocessing時,它會啟動一個新的伺服器程序。之後,每當使用者程式碼想要建立一個新的程序時,它會請求forkserver去fork一個新的程序。伺服器程序在乾淨的狀態下執行,不包含程式可能改變的全域性狀態,從而避免了一些由`fork`引起的問題。

五、關於獲取和設定當前是用什麼方式啟動程序

import multiprocessing

current_method = multiprocessing.get_start_method()
print('Current start method:', current_method) # 獲取當前啟動多程序的方法

multiprocessing.set_start_method('fork') # 設定當前的啟動程序方法為fork,也可以是其他的方法,比如:spawn、forkserver

六、說明本文章的背景所說的,總是出現的異常

1、透過multiprocessing.get_start_method()方法得知,當前正在執行的建立程序的方法是spawn

2、因為spawn是重新執行一遍全域性的程式碼,所以導致了上述的報錯。(因為我把d2l.load_data_fashion_mnist(batch_size)寫在全域性當中,所以每個子程序都會重新再執行一遍)

3、當將啟動程序方法設定為fork之後,那麼會有對應的警告:UserWarning: Cannot set number of intraop threads after parallel work has started or after set_num_threads call when using native parallel backend (Triggered internally at /Users/runner/work/pytorch/pytorch/pytorch/aten/src/ATen/ParallelNative.cpp:230.)

即:試圖在Python程式碼中使用PyTorch時更改並行工作已開始之後的內部操作執行緒數量。當你嘗試設定執行緒數,而並行任務已經開始或者`set_num_threads`函式已經被呼叫時,就會觸發這個警告。

所以編寫python時候,要明確知道當前是怎樣的建立程序的方式。並且最好不要在全域性中做各種的計算。

相關文章