在本章中,我們將更多地關注多處理和多線程之間的比較。
多cpu
它是在單個計算機系統中使用兩個或更多cpu單元。通過利用我們計算機系統中可用的全部cpu內核,這是從硬件中充分發揮潛力的最佳方法。
多線程
通過并發執行多個線程,cpu能夠管理操作系統的使用。多線程的主要思想是通過將進程劃分為多個線程來實現并行性。
下表顯示了它們之間的一些重要差異
sr.no. | multiprocessing | multiprogrammingv |
---|---|---|
1 | 多處理是指多個cpu同時處理多個進程。 | 多道程序同時在主存儲器中保存多個程序,并使用單個cpu同時執行它們。 |
2 | 它使用多個cpu。 | 它使用單cpu。 |
3 | 它允許并行處理。 | 進行上下文切換。 |
4 | 減少處理工作所需的時間。 | 處理工作所需的時間更多。 |
五 | 它有助于高效利用計算機系統的設備。 | 比多處理效率低。 |
6 | 通常更貴。 | 這種系統較便宜。 |
消除全球翻譯鎖(gil)的影響
在使用并發應用程序時,python中存在一個名為 gil(全局解釋器鎖)的限制 。gil從不允許我們使用多個cpu內核,因此我們可以說python中沒有真正的線程。gil是互斥鎖 - 互斥鎖,它使線程安全。換句話說,我們可以說gil阻止多個線程并行執行python代碼。鎖一次只能由一個線程保存,如果我們想要執行一個線程,那么它必須首先獲取鎖。
通過使用多處理,我們可以有效地繞過gil引起的限制 -
- 通過使用多處理,我們正在利用多個流程的能力,因此我們正在利用gil的多個實例。
- 因此,在任何時候都沒有限制在我們的程序中執行一個線程的字節碼。
在python中啟動進程
以下三種方法可用于在多處理模塊中以python啟動進程 -
- fork
- spawn
- forkserver
使用fork創建進程
fork命令是unix中的標準命令。它用于創建稱為子進程的新進程。此子進程與稱為父進程的進程同時運行。這些子進程也與其父進程相同,并繼承了父進程可用的所有資源。使用fork創建進程時使用以下系統調用
- fork() - 這是一個通常在內核中實現的系統調用。 它用于創建process.p>的副本
- getpid() - 此系統調用返回調用進程的進程id(pid)。
例
以下python腳本示例將幫助您了解如何創建新的子進程并獲取子進程和父進程的pid -
import os def child(): n = os.fork() if n > 0: print("pid of parent process is : ", os.getpid()) else: print("pid of child process is : ", os.getpid()) child()
產量
pid of parent process is : 25989 pid of child process is : 25990
使用spawn創建進程
spawn意味著開始新事物。因此,產生進程意味著父進程創建新進程。父進程以異步方式繼續執行,或等待子進程結束執行。按照以下步驟生成流程 -
- 導入多處理模塊。
- 創建對象流程。
- 通過調用 start() 方法啟動進程活動。
- 等待進程完成其工作并通過調用 join() 方法退出。
例
以下python腳本示例有助于生成三個進程
import multiprocessing def spawn_process(i): print ('this is process: %s' %i) return if __name__ == '__main__': process_jobs = [] for i in range(3): p = multiprocessing.process(target = spawn_process, args = (i,)) process_jobs.append(p) p.start() p.join()
輸出
this is process: 0 this is process: 1 this is process: 2
使用forkserver創建進程
forkserver機制僅適用于那些支持通過unix管道傳遞文件描述符的所選unix平臺。考慮以下幾點來理解forkserver機制的工作原理 -
- 使用forkserver機制實例化服務器以啟動新進程。
- 然后,服務器接收命令并處理創建新進程的所有請求。
- 為了創建一個新進程,我們的python程序將向forkserver發送一個請求,它將為我們創建一個進程。
- 最后,我們可以在我們的程序中使用這個新創建的過程。
守護程序在python中處理
python 多處理 模塊允許我們通過其守護進程選項獲得守護進程。守護程序進程或在后臺運行的進程遵循與守護程序線程類似的概念。要在后臺執行該過程,我們需要將守護程序標志設置為true。只要主進程正在執行,守護程序進程將繼續運行,并且它將在完成執行后或主程序被終止時終止。
例
在這里,我們使用與守護程序線程中使用的相同的示例。唯一的區別是模塊從變化 多線程 來 多 和惡魔的標志設置為true。但是,輸出會有變化,如下所示
import multiprocessing import time def nondaemonprocess(): print("starting my process") time.sleep(8) print("ending my process") def daemonprocess(): while true: print("hello") time.sleep(2) if __name__ == '__main__': nondaemonprocess = multiprocessing.process(target = nondaemonprocess) daemonprocess = multiprocessing.process(target = daemonprocess) daemonprocess.daemon = true nondaemonprocess.daemon = false daemonprocess.start() nondaemonprocess.start()
輸出
starting my process ending my process
與守護程序線程生成的輸出相比,輸出是不同的,因為沒有守護程序模式的進程具有輸出。因此,守護進程在主程序結束后自動結束,以避免運行進程的持久性。
在python中終止進程
我們可以使用 terminate() 方法立即終止或終止進程。我們將在完成執行之前立即使用此方法終止在函數幫助下創建的子進程。
例
import multiprocessing import time def child_process(): print ('starting function') time.sleep(5) print ('finished function') p = multiprocessing.process(target = child_process) p.start() print("my process has terminated, terminating main thread") print("terminating child process") p.terminate() print("child process successfully terminated")
輸出
my process has terminated, terminating main thread terminating child process child process successfully terminated
輸出顯示程序在執行子進程之前終止,該子進程是在child_process()函數的幫助下創建的。這意味著子進程已成功終止。
在python中識別當前進程
操作系統中的每個進程都具有稱為pid的進程標識。在python中,我們可以借助以下命令找出當前進程的pid
import multiprocessing print(multiprocessing.current_process().pid)
例
下面的python腳本示例有助于找出主進程的pid以及子進程的pid
import multiprocessing import time def child_process(): print("pid of child process is: {}".format(multiprocessing.current_process().pid)) print("pid of main process is: {}".format(multiprocessing.current_process().pid)) p = multiprocessing.process(target=child_process) p.start() p.join()
輸出
pid of main process is: 9401 pid of child process is: 9402
在子類中使用進程
我們可以通過對 threading.thread 類進行子類化來創建線程。此外,我們還可以通過對 multiprocessing.process 類進行子類化來創建進程。對于在子類中使用進程,我們需要考慮以下幾點 -
- 我們需要定義 process 類的新子類。
- 我們需要覆蓋 init(self [,args]) 類。
- 我們需要覆蓋 run(self [,args]) 方法來實現什么 process
- 我們需要通過調用 start() 方法來啟動該過程。
例
import multiprocessing class myprocess(multiprocessing.process): def run(self): print ('called run method in process: %s' %self.name) return if __name__ == '__main__': jobs = [] for i in range(5): p = myprocess() jobs.append(p) p.start() p.join()
輸出
called run method in process: myprocess-1 called run method in process: myprocess-2 called run method in process: myprocess-3 called run method in process: myprocess-4 called run method in process: myprocess-5
python多處理模塊 - 池類
如果我們在python應用程序中討論簡單的并行 處理 任務,那么多處理模塊為我們提供了pool類。 pool 類的以下方法可用于在主程序中啟動子進程的數量
apply()方法
此方法類似于 .threadpoolexecutor的.submit() 方法 。 它會阻塞,直到結果準備就緒。
apply_async()方法
當我們需要并行執行任務時,我們需要使用 apply_async() 方法將任務提交到池中。它是一個異步操作,在執行所有子進程之前不會鎖定主線程。
map()方法
就像 apply() 方法一樣,它也會阻塞,直到結果準備好。它等同于內置的 map() 函數,該函數將可迭代數據拆分為多個塊,并作為單獨的任務提交給進程池。
map_async()方法
它是 map() 方法的變體,因為 apply_async() 是 apply() 方法。它返回一個結果對象。結果準備就緒后,將對其應用可調用對象。必須立即完成贖回; 否則,處理結果的線程將被阻止。
例
以下示例將幫助您實現用于執行并行執行的進程池。通過 multiprocessing.pool 方法應用 square() 函數,已經執行了簡單的數字 平方 計算。然后 pool.map() 用于提交5,因為input是0到4之間的整數列表。結果將存儲在 p_outputs中 并打印出來。
def square (n): result = n*n return result if __name__ == '__main__': inputs = list(range(5)) p = multiprocessing.pool(processes = 4) p_outputs = pool.map(function_square, inputs) p.close() p.join() print ('pool :', p_outputs)
輸出
pool : [0, 1, 4, 9, 16]