python多工-執行緒
目錄
多工的概念
什麼叫“多工”呢?簡單地說,就是作業系統可以同時執行多個任務。打個比方,你一邊在用瀏覽器上網,一邊在聽MP3,一邊在用Word趕作業,這就是多工,至少同時有3個任務正在執行。還有很多工悄悄地在後臺同時執行著,只是桌面上沒有顯示而已。
現在,多核CPU已經非常普及了,但是,即使過去的單核CPU,也可以執行多工。由於CPU執行程式碼都是順序執行的,那麼,單核CPU是怎麼執行多工的呢?
答案就是作業系統輪流讓各個任務交替執行,任務1執行0.01秒,切換到任務2,任務2執行0.01秒,再切換到任務3,執行0.01秒……這樣反覆執行下去。表面上看,每個任務都是交替執行的,但是,由於CPU的執行速度實在是太快了,我們感覺就像所有任務都在同時執行一樣。
真正的並行執行多工只能在多核CPU上實現,但是,由於任務數量遠遠多於CPU的核心數量,所以,作業系統也會自動把很多工輪流排程到每個核心上執行。
注意:
- 併發:指的是任務數多餘cpu核數,通過作業系統的各種任務排程演算法,實現用多個任務“一起”執行(實際上總有一些任務不在執行,因為切換任務的速度相當快,看上去一起執行而已)
- 並行:指的是任務數小於等於cpu核數,即任務真的是一起執行的
執行緒基礎
python的thread模組是比較底層的模組,python的threading模組是對thread做了一些包裝的,可以更加方便的被使用
單執行緒執行
import time
def test():
print("test...")
time.sleep(1)
if __name__ == '__main__':
for i in range(5):
test()
執行效果:程式在控制檯每隔一秒列印test...
多執行緒執行
import time import threading def test(): print("test...") time.sleep(1) if __name__ == '__main__': for i in range(5): t = threading.Thread(target=test) t.start() # 啟動執行緒
執行效果:程式在控制檯一下子輸出五行test...
,等待1秒左右結束
說明:
- 可以明顯看出使用了多執行緒併發的操作,花費時間要短很多
- 當呼叫
start()
時,才會真正的建立執行緒,並且開始執行
主執行緒會等待所有子執行緒結束後才結束
import time
import threading
def playPhone():
print('玩手機...')
time.sleep(1)
def eat():
print("吃東西...")
time.sleep(1)
if __name__ == '__main__':
print("--開始--")
t1 = threading.Thread(target=playPhone)
t1.start()
t2 = threading.Thread(target=eat)
t2.start()
print('--執行結束')
執行效果:主執行緒阻塞1秒左右後程式結束,說明主執行緒在等待其他執行緒執行完畢。
檢視執行緒數量
print('玩手機...')
time.sleep(1)
def eat():
for i in range(10):
print("吃東西...")
time.sleep(1)
if __name__ == '__main__':
print("--開始--")
t1 = threading.Thread(target=playPhone)
t1.start()
t2 = threading.Thread(target=eat)
t2.start()
# 檢視正在執行的執行緒數量
while True:
length = len(threading.enumerate())
print("當前執行的執行緒數量:%d" % length)
print("這些執行緒是:%s" % str(threading.enumerate()))
if length == 1:
break
time.sleep(0.5)
print('--執行結束')
在python中,呼叫threading.enumerate()
能獲取當前正在執行的所有執行緒,返回值是一個list,呼叫length()
函式並傳入該list物件就獲取到當前執行執行緒的數量。
執行緒-注意點
執行緒執行程式碼的封裝
通過上一篇,能夠看出,通過使用threading模組能完成多工的程式開發,為了讓每個執行緒的封裝性更完美,所以使用threading模組時,往往會定義一個新的子類class,只要繼承threading.Thread
就可以了,然後重寫run
方法。
示例如下:
import time
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(5):
time.sleep(1)
print("我是%[email protected]%d" % (self.name, i))
if __name__ == "__main__":
mt = MyThread()
mt.start()
執行結果如下:
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
python的threading.Thread類有一個run方法,用於定義執行緒的功能函式,可以在自己的執行緒類中覆蓋該方法。而建立自己的執行緒例項後,通過Thread類的start方法,可以啟動該執行緒,交給python虛擬機器進行排程,當該執行緒獲得執行的機會時,就會呼叫run方法執行執行緒。
執行緒的執行順序
mport time
import threading
class MyThread(threading.Thread):
def run(self):
for i in range(5):
time.sleep(1)
print("我是%[email protected]%d" % (self.name, i))
if __name__ == "__main__":
for i in range(5):
mt = MyThread()
mt.start()
執行結果(執行的結果可能不一樣,但是大體是一致的):
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
我是[email protected]
從程式碼和執行結果我們可以看出,多執行緒程式的執行順序是不確定的。當執行到sleep語句時,執行緒將被阻塞(Blocked),到sleep結束後,執行緒進入就緒(Runnable)狀態,等待排程。而執行緒排程將自行選擇一個執行緒執行。上面的程式碼中只能保證每個執行緒都執行完整個run函式,但是執行緒的啟動順序、run函式中每次迴圈的執行順序都不能確定。
總結
- 每個執行緒預設有一個名字,儘管上面的例子中沒有指定執行緒物件的name,但是python會自動為執行緒指定一個名字。
- 當執行緒的run()方法結束時該執行緒完成。
- 無法控制執行緒排程程式,但可以通過別的方式來影響執行緒排程的方式。