使用Python實現(xiàn)多線程和多處理方法

在本教程中,我們將學(xué)習(xí)如何使用Python實現(xiàn)多線程和多處理方法。這些方法指導(dǎo)操作系統(tǒng)優(yōu)化使用系統(tǒng)硬件,從而提高代碼執(zhí)行效率。多線程引用Wiki的解釋—在計算機(jī)體系結(jié)構(gòu)中,多線程是指從軟件或者硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計算機(jī)因有硬件支持而能夠在同一時間執(zhí)行多個線程,進(jìn)而提升整體處理性能。并發(fā)指的是可以實現(xiàn)多個進(jìn)程的并行執(zhí)行,從而實現(xiàn)更快的運行時間。當(dāng)執(zhí)行基于I/O的任務(wù)(如下載圖像和文件)時,多線程是更有效的,另一方面多處理也適合于基于CPU的計算密集型任務(wù)。Python中的多線程實現(xiàn)為了實現(xiàn)多線程,我們將使用Python的標(biāo)準(zhǔn)庫threading。默認(rèn)情況下,該庫Python會默認(rèn)安裝,因此可以直接在代碼中導(dǎo)入。為了演示多線程的有效性,我們將從Unsplash下載5幅圖像。讓我們觀察一下當(dāng)我們按順序下載這些圖像時的執(zhí)行時間:#### 導(dǎo)入請求庫import requests

#### 定義函數(shù)def down_img(name,link): data = requests.get(link).content name = f"/home/isud/DidYouKnow/Tutorial 5/{name}.jpg" with open(name, "wb") as file: file.write(data)

#### 連續(xù)下載5張圖片%%timeit -n1 -r1images = ['https://images.unsplash.com/photo-1531458999205-f31f14fa217b', 'https://images.unsplash.com/photo-1488572749058-7f52dd70e0fa', 'https://images.unsplash.com/photo-1531404610614-68f9e73e35db', 'https://images.unsplash.com/photo-1523489405193-3884f5ca475f', 'https://images.unsplash.com/photo-1565098735462-5db3412ac4cb']for i,link in enumerate(images): down_img(i,link)

#### %%timeit results51.4 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)可以觀察到,5張圖片的完整下載耗時51.4秒,而且只有在上一次下載結(jié)束后才開始新的下載?,F(xiàn)在讓我們看看多線程如何提高代碼性能。#### 導(dǎo)入必要的庫import threadingimport requests

#### 定義函數(shù)def down_img(name,link): data = requests.get(link).content name = f"/home/isud/DidYouKnow/Tutorial 5/{name}.jpg" with open(name, "wb") as file: file.write(data)

#### 并行線程下載圖像%%timeit -n1 -r1threads = []images = ['https://images.unsplash.com/photo-1531458999205-f31f14fa217b', 'https://images.unsplash.com/photo-1488572749058-7f52dd70e0fa', 'https://images.unsplash.com/photo-1531404610614-68f9e73e35db', 'https://images.unsplash.com/photo-1523489405193-3884f5ca475f', 'https://images.unsplash.com/photo-1565098735462-5db3412ac4cb']for i,link in enumerate(images): t = threading.Thread(target=down_img, args=(i,link)) t.start() threads.a(chǎn)ppend(t)

for thread in threads: thread.join()

#### %%timeit results25.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)代碼解釋-定義圖像下載循環(huán):第1步(線程初始化)——Python在一個線程中運行完整的代碼(我們稱之為主線程)。在本例中,通過從線程庫調(diào)用Thread函數(shù),我們啟動并行線程并為它們分配一個要執(zhí)行的目標(biāo)進(jìn)程(在本例中為down_image)。被調(diào)用函數(shù)所需的所有參數(shù)都應(yīng)作為序列對象(在本例中為元組)傳遞。對Thread函數(shù)的每次調(diào)用都會啟動一個新線程(我們稱之為并行線程)。第2步(線程啟動)——調(diào)用線程的start方法將指示Python啟動線程執(zhí)行。如果for循環(huán)在主線程中執(zhí)行,而函數(shù)調(diào)用在并行線程中,則for循環(huán)的執(zhí)行將在圖片下載過程中繼續(xù)執(zhí)行。步驟3(線程的join)——每個新線程都被捕獲到一個名為threads的列表中,然后通過調(diào)用join方法將并行線程連接到主線程。為什么需要使用join?在第2步之前,我們所有的線程(主線程和并行線程)都是并行執(zhí)行的,在這種情況下,主線程完成任務(wù)的時間可以比并行線程完成任務(wù)早很多,及主線程會結(jié)束更早。為了避免這種情況,將并行線程連接到主線程是必須的,這將確保只有在并行線程完成之后才完成主線程的執(zhí)行。下圖說明了這兩種情況:

可以看出,下載圖像的執(zhí)行時間減少了近50%(大約25.6秒)。上面的示例展示了多線程在I/O操作中的幫助,以及如何提高下載/上傳過程的效率。多處理與在單個進(jìn)程中執(zhí)行多個線程的多線程不同,多處理為每個任務(wù)啟動一個新的并行進(jìn)程。如前所述,它為CPU密集型任務(wù)(需要大量計算的任務(wù))提供了相當(dāng)大的運行時改進(jìn)。在Python中實現(xiàn)多處理multiprocessing是另一個在Python中支持多處理特性的標(biāo)準(zhǔn)庫,為了理解它的功能,我們將多次調(diào)用一個計算密集型函數(shù),來計算從1到1千萬的數(shù)字的平方。此函數(shù)并行執(zhí)行8次,讓我們觀察一下這個函數(shù)在正常情況下的性能。#### 導(dǎo)入時間庫import time

#### 定義函數(shù)def demo_func(num): for i in range(num): a = i**2

#### 順序調(diào)用演示函數(shù)%%timeit -n1 -r1for i in range(8): demo_func(10000000)

#### %%timeit 結(jié)果21.2 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)演示函數(shù)的順序執(zhí)行總共花費了21.2秒,現(xiàn)在讓我們檢查在多處理設(shè)置中執(zhí)行此操作時的性能提升。#### 導(dǎo)入庫import time

#### 定義函數(shù)def demo_func(num): for i in range(num): a = i**2

#### 多處理demo函數(shù)%%timeit -n1 -r1processes = []lop_size = [10000000,10000000,10000000,10000000,10000000,10000000,10000000, 10000000]p = multiprocessing.Pool()p.map(demo_func,lop_size)p.close()p.join()

#### %%timeit 結(jié)果11.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)在多處理框架下,執(zhí)行時間減少了50%,達(dá)到11.6秒。在順序處理中,一次使用一個CPU內(nèi)核,而在多個處理中,所有系統(tǒng)內(nèi)核都是并行使用的。CPU使用率屏幕截圖顯示了相同的情況:

上圖中的每一行代表一個CPU核。請注意,在順序執(zhí)行中,每個函數(shù)調(diào)用都會觸發(fā)一個核,而在并行執(zhí)行中,所有核都是同時觸發(fā)的。代碼說明步驟1(池創(chuàng)建)-池方法創(chuàng)建可并行利用的進(jìn)程池。在沒有任何參數(shù)的情況下,創(chuàng)建的進(jìn)程數(shù)等于系統(tǒng)上的CPU核數(shù)。我有一個四核系統(tǒng),這意味著,在執(zhí)行時的8個函數(shù)調(diào)用中,前4個調(diào)用將并行運行,然后是下4個函數(shù)調(diào)用。請注意,你還可以在池中定義一個自定義的進(jìn)程數(shù)(多于內(nèi)核數(shù)),但超過某個值時,它將開始占用系統(tǒng)內(nèi)存并可能降低性能步驟2(池映射)-這是指示進(jìn)程執(zhí)行特定函數(shù)(第一個參數(shù))以及要傳遞給它的參數(shù)列表(第二個參數(shù))步驟3(Pool Close)-Close方法指示Python解釋器,我們已經(jīng)提交了要提交給池的所有內(nèi)容,將來不再向池提供更多的輸入。步驟4(池連接)-與線程的情況一樣,Join方法確保代碼執(zhí)行只在所有并行進(jìn)程完成后完成。從上面的場景中,我們可以看到多處理是如何在高效的代碼性能方面成為一個很好的幫手。結(jié)束本教程中,我們將重點放在通過優(yōu)化系統(tǒng)硬件來提高代碼性能上。希望這篇教程能幫助你,你能學(xué)到一些新東西。

(免責(zé)聲明:本網(wǎng)站內(nèi)容主要來自原創(chuàng)、合作伙伴供稿和第三方自媒體作者投稿,凡在本網(wǎng)站出現(xiàn)的信息,均僅供參考。本網(wǎng)站將盡力確保所提供信息的準(zhǔn)確性及可靠性,但不保證有關(guān)資料的準(zhǔn)確性及可靠性,讀者在使用前請進(jìn)一步核實,并對任何自主決定的行為負(fù)責(zé)。本網(wǎng)站對有關(guān)資料所引致的錯誤、不確或遺漏,概不負(fù)任何法律責(zé)任。
任何單位或個人認(rèn)為本網(wǎng)站中的網(wǎng)頁或鏈接內(nèi)容可能涉嫌侵犯其知識產(chǎn)權(quán)或存在不實內(nèi)容時,應(yīng)及時向本網(wǎng)站提出書面權(quán)利通知或不實情況說明,并提供身份證明、權(quán)屬證明及詳細(xì)侵權(quán)或不實情況證明。本網(wǎng)站在收到上述法律文件后,將會依法盡快聯(lián)系相關(guān)文章源頭核實,溝通刪除相關(guān)內(nèi)容或斷開相關(guān)鏈接。 )

贊助商
2020-09-04
使用Python實現(xiàn)多線程和多處理方法
在本教程中,我們將學(xué)習(xí)如何使用Python實現(xiàn)多線程和多處理方法。這些方法指導(dǎo)操作系統(tǒng)優(yōu)化使用系統(tǒng)硬件,從而提高代碼執(zhí)行效率。

長按掃碼 閱讀全文