使用Python實現多線程和多處理方法

使用Python實現多線程和多處理方法
在本教程中,咱們將學習如何使用Python實現多線程和多處理方法。這些方法指導操做系統優化使用系統硬件,從而提升代碼執行效率。
多線程
引用Wiki的解釋—在計算機體系結構中,多線程是指從軟件或者硬件上實現多個線程併發執行的技術。具備多線程能力的計算機因有硬件支持而可以在同一時間執行多個線程,進而提高總體處理性能。
併發指的是能夠實現多個進程的並行執行,從而實現更快的運行時間。當執行基於I/O的任務(以下載圖像和文件)時,多線程是更有效的,另外一方面多處理也適合於基於CPU的計算密集型任務。
Python中的多線程實現
爲了實現多線程,咱們將使用Python的標準庫threading。默認狀況下,該庫Python會默認安裝,所以能夠直接在代碼中導入。
爲了演示多線程的有效性,咱們將從Unsplash下載5幅圖像。讓咱們觀察一下當咱們按順序下載這些圖像時的執行時間:python

#### 導入請求庫
import requests

#### 定義函數
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)

#### 連續下載5張圖片
%%timeit -n1 -r1
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):
    down_img(i,link)

#### %%timeit results
51.4 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

能夠觀察到,5張圖片的完整下載耗時51.4秒,並且只有在上一次下載結束後纔開始新的下載。如今讓咱們看看多線程如何提升代碼性能。多線程

#### 導入必要的庫
import threading
import requests

#### 定義函數
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 -r1
threads = []
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.append(t)

for thread in threads:
    thread.join()

#### %%timeit results
25.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

代碼解釋-定義圖像下載循環:
第1步(線程初始化)——Python在一個線程中運行完整的代碼(咱們稱之爲主線程)。在本例中,經過從線程庫調用Thread函數,咱們啓動並行線程併爲它們分配一個要執行的目標進程(在本例中爲down_image)。被調用函數所需的全部參數都應做爲序列對象(在本例中爲元組)傳遞。對Thread函數的每次調用都會啓動一個新線程(咱們稱之爲並行線程)。
第2步(線程啓動)——調用線程的start方法將指示Python啓動線程執行。若是for循環在主線程中執行,而函數調用在並行線程中,則for循環的執行將在圖片下載過程當中繼續執行。
步驟3(線程的join)——每一個新線程都被捕獲到一個名爲threads的列表中,而後經過調用join方法將並行線程鏈接到主線程。
爲何須要使用join?
在第2步以前,咱們全部的線程(主線程和並行線程)都是並行執行的,在這種狀況下,主線程完成任務的時間能夠比並行線程完成任務早不少,及主線程會結束更早。
爲了不這種狀況,將並行線程鏈接到主線程是必須的,這將確保只有在並行線程完成以後才完成主線程的執行。下圖說明了這兩種狀況:
使用Python實現多線程和多處理方法
能夠看出,下載圖像的執行時間減小了近50%(大約25.6秒)。上面的示例展現了多線程在I/O操做中的幫助,以及如何提升下載/上傳過程的效率。
多處理
與在單個進程中執行多個線程的多線程不一樣,多處理爲每一個任務啓動一個新的並行進程。如前所述,它爲CPU密集型任務(須要大量計算的任務)提供了至關大的運行時改進。
在Python中實現多處理
multiprocessing是另外一個在Python中支持多處理特性的標準庫,爲了理解它的功能,咱們將屢次調用一個計算密集型函數,來計算從1到1千萬的數字的平方。此函數並行執行8次,讓咱們觀察一下這個函數在正常狀況下的性能。併發

#### 導入時間庫
import time

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

#### 順序調用演示函數
%%timeit -n1 -r1
for i in range(8):
    demo_func(10000000)

#### %%timeit 結果
21.2 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

演示函數的順序執行總共花費了21.2秒,如今讓咱們檢查在多處理設置中執行此操做時的性能提高。app

#### 導入庫
import time

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

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

#### %%timeit 結果
11.6 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)

在多處理框架下,執行時間減小了50%,達到11.6秒。在順序處理中,一次使用一個CPU內核,而在多個處理中,全部系統內核都是並行使用的。CPU使用率屏幕截圖顯示了相同的狀況:
使用Python實現多線程和多處理方法
上圖中的每一行表明一個CPU核。請注意,在順序執行中,每一個函數調用都會觸發一個核,而在並行執行中,全部核都是同時觸發的。
代碼說明
步驟1(池建立)-池方法建立可並行利用的進程池。在沒有任何參數的狀況下,建立的進程數等於系統上的CPU核數。我有一個四核系統,這意味着,在執行時的8個函數調用中,前4個調用將並行運行,而後是下4個函數調用。請注意,你還能夠在池中定義一個自定義的進程數(多於內核數),但超過某個值時,它將開始佔用系統內存並可能下降性能
步驟2(池映射)-這是指示進程執行特定函數(第一個參數)以及要傳遞給它的參數列表(第二個參數)
步驟3(Pool Close)-Close方法指示Python解釋器,咱們已經提交了要提交給池的全部內容,未來再也不向池提供更多的輸入。
步驟4(池鏈接)-與線程的狀況同樣,Join方法確保代碼執行只在全部並行進程完成後完成。
從上面的場景中,咱們能夠看到多處理是如何在高效的代碼性能方面成爲一個很好的幫手。
結束
本教程中,咱們將重點放在經過優化系統硬件來提升代碼性能上。但願這篇教程能幫助你,你能學到一些新東西。
參考連接:https://towardsdatascience.com/did-you-know-how-to-make-your-python-code-run-faster-1st-installment-f317359159a1框架

相關文章
相關標籤/搜索