本文首發於知乎python
多線程理解編程
多線程是多個任務同時運行的一種方式。好比一個循環中,每一個循環看作一個任務,咱們但願第一次循環運行還沒結束時,就能夠開始第二次循環,用這種方式來節省時間。多線程
python中這種同時運行的目的是最大化利用CPU的計算能力,將不少等待時間利用起來。這也說明若是程序耗時不是由於等待時間,而是任務很是多,就是要計算那麼久,則多線程沒法改善運行時間。app
更多有關多線程理解的內容能夠參考下面資料函數
簡單使用ui
先看下面這個函數lua
import time
def myfun():
time.sleep(1)
a = 1 + 1
print(a)
複製代碼
若是咱們要運行10次這個函數,它的運行時間主要在於每次sleep
的那一秒,1 + 1
的計算是不會耗多少時間的。這種狀況能夠用多線程提升效率。spa
下面來看一下不使用多線程耗時和使用多線程的耗時線程
不使用多線程code
t = time.time()
for _ in range(5):
myfun()
print(time.time() - t)
複製代碼
獲得結果是 5.002434492111206
下面咱們使用多線程
from threading import Thread
for _ in range(5):
th = Thread(target = myfun)
th.start()
複製代碼
這樣使用多線程其實就能夠了,你會發現大概1秒,5個2
會同時出來,說明5次循環其實幾乎同時運行,5次1秒的等待時間同時進行,最後只等待了1秒。
這裏多線程只包括了兩步
Thread
增長一個線程,這裏是將每一次循環做爲一次新的線程,一個線程執行一次myfun
函數。start()
開始運行這個線程,每一個線程都須要這樣顯式開啓纔會運行。一個線程這樣開啓後,不須要等待它運行完成,就能夠繼續運行下面的程序,即下一次循環(而後又新建了第二個線程,運行未結束即開啓第三個……)這裏要注意一點:多線程是放在循環裏面的,不能定義好循環以後,從外面將它變成多線程。
讀者可能會注意到,不用多線程時是經過程序計算時間的,使用多線程卻沒有。這是由於要計算時間須要增長一些代碼,沒法展現最簡單的多線程使用,因此就先不計算時間。接下來咱們就講join()
的使用,並計算時間。
join的使用
線程的join()
方法表示等這個線程運行完畢,程序再往下運行。咱們來看下面的例子
from threading import Thread
t = time.time()
for _ in range(5):
th = Thread(target = myfun)
th.start()
th.join()
print(time.time() - t)
# 結果爲 5.0047078132629395 秒
複製代碼
這裏start()
以後立刻join()
,表示每個線程都要運行結束才能進行下一次循環,這樣就和沒有使用多線程沒有區別了。不過若是要計算多線程運行時間倒是要用到這個join()
咱們先看一下不用join()
的狀況
from threading import Thread
t = time.time()
for _ in range(5):
th = Thread(target = myfun)
th.start()
print(time.time() - t)
# 結果爲 0.0009980201721191406 秒
複製代碼
它連1秒都沒有等,就輸出告終果,並且5個2是在打印出這個以後才輸出出來的。這是由於print(time.time() - t)
是區別於那5次循環線程以外的第6個線程,它不會等待5個線程運行結束就會開始運行。因此這樣是沒法得到上面5個線程的運行時間的,咱們須要用join()
等待5個線程都運行結束。
代碼以下
from threading import Thread
t = time.time()
ths = []
for _ in range(5):
th = Thread(target = myfun)
th.start()
ths.append(th)
for th in ths:
th.join()
print(time.time() - t)
# 結果爲 1.0038363933563232
複製代碼
上面定義ths
列表存儲這些線程,最後用循環確保每個線程都已經運行完成再計算時間差。
join()
不僅是用於這種情形。當一步代碼運行依賴以前代碼運行完成時,就要加入join()
命令。
如今咱們已經學完了多線程的通常使用方法,能夠在多數場景使用了。下面來介紹一些細節
其餘
(1)線程名稱
咱們直接看下面的代碼
import threading
print(threading.current_thread().getName())
def myfun():
time.sleep(1)
print(threading.current_thread().name)
a = 1 + 1
for i in range(5):
th = threading.Thread(target = myfun, name = 'thread {}'.format(i))
th.start()
# 輸出結果
MainThread
thread 0
thread 1
thread 4
thread 3
thread 2
複製代碼
解釋一下
threading.current_thread()
表示當前線程,能夠調用name
或getName()
獲取線程名稱MainThread
,也就是主程序佔一個線程,這個線程和以後用Thread
新加的線程是相互獨立的,主線程不會等待其他線程運行結束就會繼續往下運行。以前不用join()
沒法計算運行時間就是由於主線程先運行完了。Thread
表示運行這個函數啓動一個新的線程,在其中加一個name
參數指定這個函數線程名,則在這個函數內打印線程名就顯示這裏name
參數對應值print(threading.current_thread().name)
則是MainThread
;第二種print(th.name)
則是thread 1
等(2)Thread函數
上面咱們使用了Thread函數的target name
參數,下面來講一下它的其餘參數
args
指定target
對應函數的參數,用元組傳入,好比args = (3, )
daemon
主線程默認是False
,若是沒有指定則繼承父線程的值。True
則若是主線程運行結束,該線程也中止運行;False
則該線程會繼續運行直到運行結束,無視主線程如何。(要看這個參數的效果要在py文件中編寫代碼,在cmd裏運行,不能在jupyter notebook裏,由於這裏會多出一些線程干擾)group
是預留的一個參數,用於之後擴展ThreadGroup
類,如今沒用(3)Thread對象
上面threading.Thread
和threading.current_thread()
都建立了一個Thread對象,Thread對象有以下屬性和方法
getName() .name
獲取線程名setName()
設置線程名start() join()
這兩個以前說過了join()
有一個timeout
參數,表示等待這個線程結束時,若是等待時間超過這個時間,就再也不等,繼續進行下面的代碼,可是這個線程不會被中斷run()
也是運行這個線程,可是必須等到這個線程運行結束纔會繼續執行以後的代碼(若是將上面的start
全換成run
則至關於沒有開多線程)is_alive()
若是該線程還沒運行完,就是True
不然False
daemon
返回該線程的daemon
setDaemon(True)
設置線程的daemon
(4)threading
一些直接調用的變量
threading.currentThread()
: 返回當前的線程變量threading.enumerate()
: 返回一個包含正在運行的線程的listthreading.activeCount()
: 返回正在運行的線程數量,與len(threading.enumerate())
有相同的結果專欄主頁:python編程
專欄目錄:目錄
版本說明:軟件及包版本說明