溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Python多進(jìn)程怎么應(yīng)用

發(fā)布時(shí)間:2022-10-12 15:23:14 來(lái)源:億速云 閱讀:169 作者:iii 欄目:web開(kāi)發(fā)

這篇文章主要講解了“Python多進(jìn)程怎么應(yīng)用”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Python多進(jìn)程怎么應(yīng)用”吧!

并行和串行計(jì)算

想象一下,你有一個(gè)巨大的問(wèn)題要解決,而你獨(dú)自一人。你需要計(jì)算八個(gè)不同數(shù)字的平方根。你是做什么的?你沒(méi)有太多選擇。從第一個(gè)數(shù)字開(kāi)始,然后計(jì)算結(jié)果。然后,你繼續(xù)和其他人。

如果你有三個(gè)擅長(zhǎng)數(shù)學(xué)的朋友愿意幫助你呢?他們每個(gè)人都會(huì)計(jì)算兩個(gè)數(shù)字的平方根,你的工作會(huì)更容易,因?yàn)楣ぷ髁吭谀愕呐笥阎g平均分配。這意味著你的問(wèn)題將更快地得到解決。

好了,一切都清楚了嗎?在這些示例中,每個(gè)朋友代表CPU的核心。在第一個(gè)示例中,整個(gè)任務(wù)由你依次解決。這稱(chēng)為串行計(jì)算。在第二個(gè)示例中,由于你總共使用了四個(gè)內(nèi)核,因此你使用的是并行計(jì)算。并行計(jì)算涉及使用并行進(jìn)程或在處理器的多個(gè)核之間劃分的進(jìn)程。

Python多進(jìn)程怎么應(yīng)用

并行編程模型

我們已經(jīng)確定了什么是并行編程,但我們?nèi)绾问褂盟??我們之前說(shuō)過(guò),并行計(jì)算涉及在處理器的多個(gè)核心之間執(zhí)行多個(gè)任務(wù),這意味著這些任務(wù)是同時(shí)執(zhí)行的。在進(jìn)行并行化之前,你應(yīng)該考慮幾個(gè)問(wèn)題。例如,是否有其他優(yōu)化可以加快我們的計(jì)算速度?

現(xiàn)在,讓我們理所當(dāng)然地認(rèn)為并行化是最適合的解決方案。并行計(jì)算主要有三種模式:

  • 完全平行。任務(wù)可以獨(dú)立運(yùn)行,不需要相互通信。

  • 共享內(nèi)存并行。進(jìn)程(或線程)需要通信,因此它們共享一個(gè)全局地址空間。

  • 消息傳遞。進(jìn)程需要在需要時(shí)共享消息。

在本文中,我們將說(shuō)明第一個(gè)模型,它也是最簡(jiǎn)單的。

Python多進(jìn)程:Python中基于進(jìn)程的并行性

在 Python 中實(shí)現(xiàn)并行性的一種方法是使用multiprocessing 模塊。multiprocessing模塊允許你創(chuàng)建多個(gè)進(jìn)程,每個(gè)進(jìn)程都有自己的 Python 解釋器。因此,Python 多進(jìn)程實(shí)現(xiàn)了基于進(jìn)程的并行。

你可能聽(tīng)說(shuō)過(guò)其他庫(kù),比如threading,它也是Python內(nèi)置的,但它們之間有著重要的區(qū)別。multiprocessing模塊創(chuàng)建新進(jìn)程,而threading創(chuàng)建新線程。

使用多進(jìn)程的好處

你可能會(huì)問(wèn),“為什么選擇多進(jìn)程?” 多進(jìn)程可以通過(guò)并行而不是按順序運(yùn)行多個(gè)任務(wù)來(lái)顯著提高程序的效率。一個(gè)類(lèi)似的術(shù)語(yǔ)是多線程,但它們是不同的。

進(jìn)程是加載到內(nèi)存中運(yùn)行的程序,不與其他進(jìn)程共享其內(nèi)存。線程是進(jìn)程中的一個(gè)執(zhí)行單元。多個(gè)線程在一個(gè)進(jìn)程中運(yùn)行,并相互共享進(jìn)程的內(nèi)存空間。

Python的全局解釋器鎖(GIL)只允許在解釋器下一次運(yùn)行一個(gè)線程,這意味著如果需要Python解釋器,你將無(wú)法享受多線程的性能優(yōu)勢(shì)。這就是在Python中多進(jìn)程比線程更占優(yōu)勢(shì)的原因。多個(gè)進(jìn)程可以并行運(yùn)行,因?yàn)槊總€(gè)進(jìn)程都有自己的解釋器,執(zhí)行分配給它的指令。此外,操作系統(tǒng)將在多個(gè)進(jìn)程中查看你的程序,并分別對(duì)它們進(jìn)行調(diào)度,即,你的程序在總的計(jì)算機(jī)資源中占有更大的份額。因此,當(dāng)程序受到CPU限制時(shí),多進(jìn)程速度更快。在程序中有大量I/O的情況下,線程可能更高效,因?yàn)榇蠖鄶?shù)時(shí)候,程序都在等待I/O完成。然而,多進(jìn)程通常效率更高,因?yàn)樗瑫r(shí)運(yùn)行。

以下是多進(jìn)程的一些好處:

  • 在處理高CPU密集型任務(wù)時(shí)更好地使用CPU

  • 與線程相比,對(duì)子線程的控制更多

  • 易于編碼

第一個(gè)優(yōu)點(diǎn)與性能有關(guān)。由于多進(jìn)程創(chuàng)建了新的進(jìn)程,你可以通過(guò)在其他內(nèi)核之間劃分任務(wù)來(lái)更好地利用CPU的計(jì)算能力?,F(xiàn)在大多數(shù)處理器都是多核處理器,如果你優(yōu)化代碼,可以通過(guò)并行計(jì)算節(jié)省時(shí)間。

第二個(gè)優(yōu)點(diǎn)是多線程處理的替代方案。線程不是進(jìn)程,這有其后果。如果你創(chuàng)建了一個(gè)線程,那么像處理正常進(jìn)程一樣終止它甚至中斷它是很危險(xiǎn)的。由于多進(jìn)程和多線程之間的比較不在本文的范圍內(nèi),后續(xù)我會(huì)單獨(dú)寫(xiě)一篇來(lái)講講多進(jìn)程和多線程的區(qū)別。

多進(jìn)程的第三個(gè)優(yōu)點(diǎn)是它很容易實(shí)現(xiàn),因?yàn)槟銍L試處理的任務(wù)適合并行編程。

Python多進(jìn)程入門(mén)

我們終于準(zhǔn)備好編寫(xiě)一些 Python 代碼了!

我們將從一個(gè)非?;镜氖纠_(kāi)始,我們將使用它來(lái)說(shuō)明 Python 多進(jìn)程的核心方面。在此示例中,我們將有兩個(gè)進(jìn)程:

  • parent經(jīng)常。只有一個(gè)父進(jìn)程,它可以有多個(gè)子進(jìn)程。

  • child進(jìn)程。這是由父進(jìn)程產(chǎn)生的。每個(gè)子進(jìn)程也可以有新的子進(jìn)程。

我們將使用該child過(guò)程來(lái)執(zhí)行某個(gè)函數(shù)。這樣,parent可以繼續(xù)執(zhí)行。

一個(gè)簡(jiǎn)單的 Python多進(jìn)程示例

這是我們將用于此示例的代碼:

from multiprocessing import Process

def bubble_sort(array):
    check = True
    while check == True:
      check = False
      for i in range(0, len(array)-1):
        if array[i] > array[i+1]:
          check = True
          temp = array[i]
          array[i] = array[i+1]
          array[i+1] = temp
    print("Array sorted: ", array)

if __name__ == '__main__':
    p = Process(target=bubble_sort, args=([1,9,4,5,2,6,8,4],))
    p.start()
    p.join()

在這個(gè)片段中,我們定義了一個(gè)名為bubble_sort(array)。這個(gè)函數(shù)是冒泡排序算法的一個(gè)非常簡(jiǎn)單的實(shí)現(xiàn)。如果你不知道它是什么,請(qǐng)不要擔(dān)心,因?yàn)樗⒉恢匾?。要知道的關(guān)鍵是它是一個(gè)可以實(shí)現(xiàn)某個(gè)功能的函數(shù)。

進(jìn)程類(lèi)

multiprocessing,我們導(dǎo)入類(lèi)Process。此類(lèi)表示將在單獨(dú)進(jìn)程中運(yùn)行的活動(dòng)。事實(shí)上,你可以看到我們已經(jīng)傳遞了一些參數(shù):

  • target=bubble_sort,意味著我們的新進(jìn)程將運(yùn)行該bubble_sort函數(shù)

  • args=([1,9,4,52,6,8,4],),這是作為參數(shù)傳遞給目標(biāo)函數(shù)的數(shù)組

CDN">一旦我們創(chuàng)建了 Process 類(lèi)的實(shí)例,我們只需要啟動(dòng)該進(jìn)程。這是通過(guò)編寫(xiě)p.start()完成的。此時(shí),該進(jìn)程開(kāi)始。

在我們退出之前,我們需要等待子進(jìn)程完成它的計(jì)算。該join()方法等待進(jìn)程終止。

在這個(gè)例子中,我們只創(chuàng)建了一個(gè)子進(jìn)程。正如你可能猜到的,我們可以通過(guò)在Process類(lèi)中創(chuàng)建更多實(shí)例來(lái)創(chuàng)建更多子進(jìn)程。

進(jìn)程池類(lèi)

如果我們需要?jiǎng)?chuàng)建多個(gè)進(jìn)程來(lái)處理更多 CPU 密集型任務(wù)怎么辦?我們是否總是需要明確地開(kāi)始并等待終止?這里的解決方案是使用Pool類(lèi)。

Pool類(lèi)允許你創(chuàng)建一個(gè)工作進(jìn)程池,在下面的示例中,我們將研究如何使用它。這是我們的新示例:

from multiprocessing import Pool
import time
import math

N = 5000000

def cube(x):
    return math.sqrt(x)

if __name__ == "__main__":
    with Pool() as pool:
      result = pool.map(cube, range(10,N))
    print("Program finished!")

在這個(gè)代碼片段中,我們有一個(gè)cube(x)函數(shù),它只接受一個(gè)整數(shù)并返回它的平方根。很簡(jiǎn)單,對(duì)吧?

然后,我們創(chuàng)建一個(gè)Pool類(lèi)的實(shí)例,而不指定任何屬性。默認(rèn)情況下,Pool類(lèi)為每個(gè) CPU 核心創(chuàng)建一個(gè)進(jìn)程。接下來(lái),我們使用幾個(gè)參數(shù)運(yùn)行map方法。

map方法將cube函數(shù)應(yīng)用于我們提供的可迭代對(duì)象的每個(gè)元素——在本例中,它是從10N的每個(gè)數(shù)字的列表。

這樣做的最大優(yōu)點(diǎn)是列表上的計(jì)算是并行進(jìn)行的!

joblib

joblib是一組使并行計(jì)算更容易的工具。它是一個(gè)用于多進(jìn)程的通用第三方庫(kù)。它還提供緩存和序列化功能。要安裝joblib包,請(qǐng)?jiān)诮K端中使用以下命令:

pip install joblib

我們可以將之前的示例轉(zhuǎn)換為以下示例以供使用joblib

from joblib import Parallel, delayed
 
def cube(x):
    return x**3
 
start_time = time.perf_counter()
result = Parallel(n_jobs=3)(delayed(cube)(i) for i in range(1,1000))
finish_time = time.perf_counter()
print(f"Program finished in {finish_time-start_time} seconds")
print(result)

事實(shí)上,直觀地看到它的作用。delayed()函數(shù)是另一個(gè)函數(shù)的包裝器,用于生成函數(shù)調(diào)用的“延遲”版本。這意味著它在被調(diào)用時(shí)不會(huì)立即執(zhí)行函數(shù)。

然后,我們多次調(diào)用delayed函數(shù),并傳遞不同的參數(shù)集。例如,當(dāng)我們將整數(shù)1賦予cube函數(shù)的延遲版本時(shí),我們不計(jì)算結(jié)果,而是分別為函數(shù)對(duì)象、位置參數(shù)和關(guān)鍵字參數(shù)生成元組(cube, (1,), {})。

我們使用Parallel()創(chuàng)建了引擎實(shí)例。當(dāng)它像一個(gè)以元組列表作為參數(shù)的函數(shù)一樣被調(diào)用時(shí),它將實(shí)際并行執(zhí)行每個(gè)元組指定的作業(yè),并在所有作業(yè)完成后收集結(jié)果作為列表。在這里,我們創(chuàng)建了n_jobs=3Parallel()實(shí)例,因此將有三個(gè)進(jìn)程并行運(yùn)行。

我們也可以直接編寫(xiě)元組。因此,上面的代碼可以重寫(xiě)為:

result = Parallel(n_jobs=3)((cube, (i,), {}) for i in range(1,1000))

使用joblib的好處是,我們可以通過(guò)簡(jiǎn)單地添加一個(gè)附加參數(shù)在多線程中運(yùn)行代碼:

result = Parallel(n_jobs=3, prefer="threads")(delayed(cube)(i) for i in range(1,1000))

這隱藏了并行運(yùn)行函數(shù)的所有細(xì)節(jié)。我們只是使用與普通列表理解沒(méi)有太大區(qū)別的語(yǔ)法。

充分利用 Python多進(jìn)程

創(chuàng)建多個(gè)進(jìn)程并進(jìn)行并行計(jì)算不一定比串行計(jì)算更有效。對(duì)于 CPU 密集度較低的任務(wù),串行計(jì)算比并行計(jì)算快。因此,了解何時(shí)應(yīng)該使用多進(jìn)程非常重要——這取決于你正在執(zhí)行的任務(wù)。

為了讓你相信這一點(diǎn),讓我們看一個(gè)簡(jiǎn)單的例子:

from multiprocessing import Pool
import time
import math

N = 5000000

def cube(x):
    return math.sqrt(x)

if __name__ == "__main__":
    # first way, using multiprocessing
    start_time = time.perf_counter()
    with Pool() as pool:
      result = pool.map(cube, range(10,N))
    finish_time = time.perf_counter()
    print("Program finished in {} seconds - using multiprocessing".format(finish_time-start_time))
    print("---")
    # second way, serial computation
    start_time = time.perf_counter()
    result = []
    for x in range(10,N):
      result.append(cube(x))
    finish_time = time.perf_counter()
    print("Program finished in {} seconds".format(finish_time-start_time))

此代碼段基于前面的示例。我們正在解決同樣的問(wèn)題,即計(jì)算N個(gè)數(shù)的平方根,但有兩種方法。第一個(gè)涉及 Python 進(jìn)程的使用,而第二個(gè)不涉及。我們使用time庫(kù)中的perf_counter()方法來(lái)測(cè)量時(shí)間性能。

在我的電腦上,我得到了這個(gè)結(jié)果:

> python code.py
Program finished in 1.6385094 seconds - using multiprocessing
---
Program finished in 2.7373942999999996 seconds

如你所見(jiàn),相差不止一秒。所以在這種情況下,多進(jìn)程更好。

讓我們更改代碼中的某些內(nèi)容,例如N的值。 讓我們把它降低到N=10000,看看會(huì)發(fā)生什么。

這就是我現(xiàn)在得到的:

> python code.py
Program finished in 0.3756742 seconds - using multiprocessing
---
Program finished in 0.005098400000000003 seconds

發(fā)生了什么?現(xiàn)在看來(lái),多進(jìn)程是一個(gè)糟糕的選擇。為什么?

與解決的任務(wù)相比,在進(jìn)程之間拆分計(jì)算所帶來(lái)的開(kāi)銷(xiāo)太大了。你可以看到在時(shí)間性能方面有多大差異。

感謝各位的閱讀,以上就是“Python多進(jìn)程怎么應(yīng)用”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Python多進(jìn)程怎么應(yīng)用這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI