1. 程式人生 > >python3:多執行緒(threading,Tread)

python3:多執行緒(threading,Tread)

多執行緒(threading)

執行緒是排程的最小單元. 一個程序可以包含多個執行緒.
執行緒是通過 Thread類進行例項化.

舉個例子說明多執行緒的好處:
1.我們在爬蟲的時候,輸入URL後需要做兩件事 第一要獲取列表,第二要獲取詳情
2.如果是多執行緒直接 第一,第二一起執行,第一個操作在等待的時候會把GIL交給第二個操作,
這樣不要等待返回,就可以執行第二個操作,大大節省了時間,這樣就實現了併發.
如果不是多執行緒會先執行第一個操作等待返回結果再執行第二個操作.

接下來用程式碼進行演示下效果:

import time,threading   #匯入執行緒 moudle

def get_details_html(url):   #執行的函式1
    print("html start")
    time.sleep(5)        #為了模擬返回的時間
    print("html end")
def get_details_url(url):  #執行的函式 2  
    print("url start")
    time.sleep(5)       #為了模擬返回的時間
    print("url end")

if  __name__=="__main__":
    thread1=threading.Thread(target=get_details_html,args=("",))
    #建立執行緒1
    thread2=threading.Thread(target=get_details_url,args=("",))
    # 建立執行緒2
    start_time=time.time()
    thread1.start()   #開啟執行緒1
    thread2.start()   #開啟執行緒2
    run_time=time.time()-start_time   #記錄執行時間
    print("runtime:{}".format(run_time))  #列印執行時間  

列印結果如下:

html start
url start
runtime:0.0
html end
url end

從列印結果了看,印證我們的說法,在等待返回會把GIL 鎖釋放出來.

為什麼 runtime 為0 呢? 按照我們的理解併發結果應該為3 .

下邊我們來解釋下 :

這裡面隱藏了一個知識點就是我們都忽略了主執行緒.

實際 一共有三個執行緒 執行緒1,執行緒2 和主執行緒 main. 我們來debug 看下 :

一共有三個執行緒
三個執行緒之間是併發的,所以main 執行緒不會等待其他兩個直接執行了,所以結果為0 .

雖然它們三個都是併發的,各自執行但是 main執行緒執行完了並沒有退出 ,一旦它斷掉,其他
兩個執行緒也會掛掉,main執行緒一掛掉,程序就掛.

setDaemaon 守護執行緒

1.演示主執行緒退出,子執行緒掛掉的情況 會用到一個知識_setDaemon 程式碼演示如下:

import time,threading   #匯入執行緒 moudle

def get_details_html(url):   #執行的函式1
    print("html start")
    time.sleep(5)        #為了模擬返回的時間
    print("html end")
def get_details_url(url):  #執行的函式 2
    print("url start")
    time.sleep(5)       #為了模擬返回的時間
    print("url end")

if  __name__=="__main__":
    thread1=threading.Thread(target=get_details_html,args=("",))
    #建立執行緒1
    thread2=threading.Thread(target=get_details_url,args=("",))
    # 建立執行緒2
    thread1.setDaemon(True)  # 當主執行緒退出,子執行緒強制退出
    thread2.setDaemon(True)  # 當主執行緒退出,子執行緒強制退出
    start_time=time.time()
    thread1.start()   #開啟執行緒1
    thread2.start()   #開啟執行緒2

    run_time=time.time()-start_time   #記錄執行時間
    print("runtime:{}".format(run_time))  #列印執行時間

列印結果:

html start
url start
runtime:0.0

當執行緒呼叫的時候,執行緒就變成了守護執行緒.
假如執行緒1 變成守護執行緒,執行緒2不是,執行緒2仍然會繼續執行完.

join()函式控制多執行緒的執行順序

假如我們都在每個執行緒加上join,看下執行效果:

import time,threading   #匯入執行緒 moudle

def get_details_html(url):   #執行的函式1
    print("html start")
    time.sleep(5)        #為了模擬返回的時間
    print("html end")
def get_details_url(url):  #執行的函式 2
    print("url start")
    time.sleep(5)       #為了模擬返回的時間
    print("url end")

if  __name__=="__main__":
    thread1=threading.Thread(target=get_details_html,args=("",))
    #建立執行緒1
    thread2=threading.Thread(target=get_details_url,args=("",))
    # 建立執行緒2
    thread1.setDaemon(True)  # 當主執行緒退出,子執行緒強制退出
    thread2.setDaemon(True)  # 當主執行緒退出,子執行緒強制退出
    start_time=time.time()
    thread1.start()   #開啟執行緒1
    thread2.start()   #開啟執行緒2
    thread1.join()    #先執行
    thread2.join()   #先執行
    run_time=time.time()-start_time   #記錄執行時間
    print("runtime:{}".format(run_time))  #列印執行時間

列印結果:

html start
url start
url end
html end
runtime:5.000763416290283

說明兩點:
1.主執行緒在最後才執行(runtime)
2.執行時間並不是 5+5 , 說明實現了併發操作.

通過繼承Thread 類來實現多執行緒

threading 適合程式碼量比較小,場景不復雜的情況下. 如果程式碼量大直接用繼承類比較簡潔 .

我們把上邊的程式碼用繼承Thread類進行改變,如下:

import time,threading   #匯入執行緒 moudle

class Get_details_html(threading.Thread):  #繼承Thread類
    def __init__(self):                                   #重寫父類init 方法,在這裡可以加入執行緒一些屬性 例如name 
        super().__init__()                            
    def run(self):                                          #重寫run方法, 並把執行緒的執行過程寫到這裡
        print("html start")
        time.sleep(5)  # 為了模擬返回的時間
        print("html end")

class Get_details_url(threading.Thread):
    def __init__(self):
        super().__init__()
    def run(self):
        print("url start")
        time.sleep(5)  # 為了模擬返回的時間
        print("url end")
if  __name__=="__main__":
    thread1=Get_details_html()    #要取到一個執行緒物件,才能執行類的方法.
    #建立執行緒1
    thread2=Get_details_url()
    # 建立執行緒2
    start_time=time.time()
    thread1.start()   #開啟執行緒1
    thread2.start()   #開啟執行緒2
    thread1.join()
    thread2.join()
    run_time=time.time()-start_time   #記錄執行時間
    print("runtime:{}".format(run_time))  #列印執行時間

列印結果:

html start
url start
url end
html end
runtime:5.001164197921753

我們發現執行結果一樣, 利用繼承類我們可以實現複雜的操作,大部分都是用這個方式.

我們已經完成了多執行緒實現的兩種方式,下一節我們將實現執行緒之間如何通訊.

謝謝瀏覽,有好的建議請留言 謝謝