在學(xué)習(xí)線程的創(chuàng)建之前,我們先來了解一下線程對象和線程體這兩個概念,線程對象就是我們通過線程模塊中的線程類創(chuàng)建的對象,而線程體就是線程執(zhí)行的相關(guān)內(nèi)容,例如指令和函數(shù)等。
線程有四個變化狀態(tài):
1) 創(chuàng)建線程
當(dāng)創(chuàng)建一個新的進程時,也創(chuàng)建一個新的線程,進程中的線程可以在同一進程中創(chuàng)建新的線程。
2) 終止線程
可以正常終止自己,也可能某個線程執(zhí)行錯誤,由其它線程強行終止。終止線程操作主要負責(zé)釋放線程占有的寄存器和棧。
3) 阻塞線程
當(dāng)線程等待某個事件無法運行時,停止其運行。
4) 喚醒線程
當(dāng)阻塞線程的事件發(fā)生時,將被阻塞的線程狀態(tài)置為就緒態(tài),將其掛到就緒隊列,進程仍然具有與執(zhí)行相關(guān)的狀態(tài)。
在Python中有兩個系統(tǒng)模塊供我們使用:_thread和threading,前者為低級模塊,后者對前者進行了封裝,通常我們會使用threading模塊。
1. threading模塊
threading是一種面向?qū)ο蟮哪K,其中使用最多的是Thread類,還有幾種比較常用的函數(shù):
threading.active_count():返回當(dāng)前活動的線程數(shù)。
threading.current_thread():返回當(dāng)前的Thread對象。
threading.main_thread():返回主線程對象。
2. Thread類
我們可以使用Thread類來代表一個線程對象,它的語法格式如下:
Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
group應(yīng)該為None,在實現(xiàn)ThreadGroup類時為將來的擴展保留。
target是run()方法要調(diào)用的可調(diào)用對象。默認為“無”,表示不調(diào)用任何內(nèi)容。
name是線程名。默認情況下,一個唯一的名稱由“Thread-N”構(gòu)成,其中N是一個小的十進制數(shù)。
args是目標(biāo)調(diào)用的參數(shù)元組,默認為()。
kwargs是目標(biāo)調(diào)用的關(guān)鍵字參數(shù)字典。默認為{}。
我們通過一個例子來看一下,代碼如下:
import threading import time def test(): for i in range(2): time.sleep(1)#設(shè)置一個等待時間 print('這是第%s線程'%threading.current_thread().name) if __name__ == '__main__': my_list = [] for i in range(5): a = threading.Thread(target=test) my_list.append(a) for j in my_list: j.start() for m in my_list: m.join()
運行結(jié)果為:
這是第Thread-2線程 這是第Thread-1線程 這是第Thread-5線程這是第Thread-4線程 這是第Thread-3線程 這是第Thread-1線程 這是第Thread-2線程 這是第Thread-5線程這是第Thread-4線程這是第Thread-3線程
通過運行結(jié)果我們可以看出,線程的執(zhí)行順序是不確定的,再加上等待時間,就會出現(xiàn)等待的空白片段,關(guān)于這個我們在操作系統(tǒng)中有很多相關(guān)內(nèi)容。
我們主要是通過threading.Thread(target=test)來創(chuàng)建線程,然后把四個線程放在一個列表中,然后我們再通過start()方法開啟線程,join()方法等待線程結(jié)束。
3. 通過Thread子類創(chuàng)建線程
我們還可以定義一個子類,使這個子類繼承Thread線程類中的方法來創(chuàng)建線程,代碼如下:
import threading import time class MyThread(threading.Thread): def __init__(self,name = None): super().__init__(name = name) def run(self): t = threading.current_thread() for i in range(4): print('第%d次執(zhí)行線程%s'%(i,t.name)) time.sleep(1) print('執(zhí)行完畢') if __name__ == '__main__': thread_one = MyThread() thread_one.start() thread_one.join() thread_two = MyThread() thread_two.start()
運行結(jié)果如下:
第0次執(zhí)行線程Thread-1第0次執(zhí)行線程Thread-1 第1次執(zhí)行線程Thread-1 第2次執(zhí)行線程Thread-1 第3次執(zhí)行線程Thread-1 執(zhí)行完畢 第0次執(zhí)行線程Thread-2 第1次執(zhí)行線程Thread-2 第2次執(zhí)行線程Thread-2 第3次執(zhí)行線程Thread-2 執(zhí)行完畢
這種方式等于先定義了一個子類,然后繼承了threading.Thread的線程類,然后定義run()方法,然后在主程序中使用我們定義的子類創(chuàng)建兩個線程,這兩個線程會自動調(diào)用run()方法,我們把線程啟動然后等待即可。
4. 總結(jié)
上面所講的是我們創(chuàng)建線程的時候最常用的兩種方式,通常我們會選擇后者去進行創(chuàng)建,在編程的時候給每個線程執(zhí)行的時間time.sleep(),這樣會通過線程暫停而給其它線程來爭搶執(zhí)行的機會,這一點我們在后面會學(xué)習(xí)到。
C語言網(wǎng)提供由在職研發(fā)工程師或ACM藍橋杯競賽優(yōu)秀選手錄制的視頻教程,并配有習(xí)題和答疑,點擊了解:
一點編程也不會寫的:零基礎(chǔ)C語言學(xué)練課程
解決困擾你多年的C語言疑難雜癥特性的C語言進階課程
從零到寫出一個爬蟲的Python編程課程
只會語法寫不出代碼?手把手帶你寫100個編程真題的編程百練課程
信息學(xué)奧賽或C++選手的 必學(xué)C++課程
藍橋杯ACM、信息學(xué)奧賽的必學(xué)課程:算法競賽課入門課程
手把手講解近五年真題的藍橋杯輔導(dǎo)課程