1. 程式人生 > >python多工-執行緒

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秒左右結束

說明:

  1. 可以明顯看出使用了多執行緒併發的操作,花費時間要短很多
  2. 當呼叫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函式中每次迴圈的執行順序都不能確定。

總結

  1. 每個執行緒預設有一個名字,儘管上面的例子中沒有指定執行緒物件的name,但是python會自動為執行緒指定一個名字。
  2. 當執行緒的run()方法結束時該執行緒完成。
  3. 無法控制執行緒排程程式,但可以通過別的方式來影響執行緒排程的方式。