如何改变 Python 中线程的执行顺序
一、主線程會等待所有的子線程結束后才結束
首先我看下最普通情況下,主線程和子線程的情況。
import threading from time import sleep, ctimedef sing():for i in range(3):print("正在唱歌...%d" % i)sleep(1)def dance():for i in range(3):print("正在跳舞...%d" % i)sleep(1)if __name__ == '__main__':print('---開始---:%s' % ctime())t1 = threading.Thread(target=sing)t2 = threading.Thread(target=dance)t1.start()t2.start()print('---結束---:%s' % ctime())運行結果:
最后一行打印的代碼就算在一開始運行了,程序也不會結束。
只有等待所有的子線程(sing 和 dance)都執行完畢,主線程才會結束,即程序結束。
二、默認狀態下,多線程的執行順序是不確定的
我們先來看一段代碼:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import threading import timeclass MyThread(threading.Thread):def run(self):for i in range(3):time.sleep(1)msg = "I'm "+self.name+' @ '+str(i)print(msg)def test():for i in range(5):t = MyThread()t.start()if __name__ == '__main__':test()運行結果:
I'm Thread-1 @ 0 I'm Thread-2 @ 0 I'm Thread-3 @ 0 I'm Thread-4 @ 0 I'm Thread-5 @ 0 I'm Thread-1 @ 1 I'm Thread-3 @ 1 I'm Thread-2 @ 1 I'm Thread-4 @ 1 I'm Thread-5 @ 1 I'm Thread-1 @ 2 I'm Thread-3 @ 2 I'm Thread-2 @ 2 I'm Thread-4 @ 2 I'm Thread-5 @ 2每次的運行結果可能都不一樣,但大體差不多。
說明:
從代碼和執行結果我們可以看出,多線程程序的執行順序是不確定的。
當執行到 sleep 語句時,線程將被阻塞,到 sleep 結束后,線程進入就緒狀態,等待調度,而線程調度將自行選擇一個線程執行。
上面的代碼中只能保證每個線程都運行完整個 run 函數,但是線程的啟動順序、run 函數中每次循環的執行順序都不能確定。
總結
每個線程默認有一個名字,盡管上面的例子中沒有指定線程對象的 name,但是 python 會自動為線程指定一個名字。
當線程的 run() 方法結束時該線程完成。
無法控制線程調度程序,但可以通過別的方式來影響線程調度的方式。
三、Python daemon 守護線程詳解
當程序中擁有多個線程時,主線程執行結束并不會影響子線程繼續執行。
換句話說,只有程序中所有線程全部執行完畢后,程序才算真正結束。
Python 還支持創建另一種線程,稱為守護線程(或后臺線程)。
此類線程的特點是,當程序中主線程及所有非守護線程執行結束時,未執行完畢的守護線程也會隨之消亡,程序將結束運行。
守護線程本質也是線程,因此其創建方式和普通線程一樣,唯一不同之處在于,將普通線程設為守護線程,需通過線程對象調用其 damon 屬性,將該屬性的值改為 True。
注意:線程對象調用 daemon 屬性必須在調用 start() 方法之前,否則 Python 解釋器將報 RuntimeError 錯誤。
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import threadingdef action(len):for i in range(len):print(threading.current_thread().getName() + "," + str(i))def main():t1 = threading.Thread(target=action, args=(10,))# 設置子線程為守護進程t1.daemon = Truet1.start()for i in range(3):print(threading.current_thread().getName()+','+str(i))if __name__ == "__main__":main()運行結果:
Thread-1,0 MainThread,0 MainThread,1 MainThread,2程序中,子線程里的程序就循環了一次,接著主線程執行完后,子線程就不打印信息了。
由于該程序中除了守護線程就只有主線程,因此只要主線程執行結束,則守護線程也隨之消亡。
四、控制線程執行順序
通過前面的學習我們知道,主線程和子線程會輪流獲得 CPU 的資源。
但有時候,我們想讓某個子線程先執行,然后再讓主線程執行代碼,該如何實現呢?
很簡單,通過調用線程對象的 join() 方法即可。
join() 方法的功能是在程序指定位置,優先讓該方法的調用者使用 CPU 資源。
該方法的語法格式如下:
thread.join( [timeout] )timeout 參數作為可選參數,其功能是指定 thread 線程最多可以霸占 CPU 資源的時間(以秒為單位)。
如果省略,則默認直到 thread 執行結束(進入死亡狀態)才釋放 CPU 資源。
我們仍舊拿上面的例子來舉例:
''' 遇到問題沒人解答?小編創建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' import threadingdef action(len):for i in range(len):print(threading.current_thread().getName() + "," + str(i))def main():t1 = threading.Thread(target=action, args=(10,))# 設置子線程為守護進程t1.daemon = Truet1.start()t1.join()for i in range(3):print(threading.current_thread().getName()+','+str(i))if __name__ == "__main__":main()我們在子線程調用的后面,添加了 t1.join()。
運行結果:
Thread-1,0 Thread-1,1 Thread-1,2 Thread-1,3 Thread-1,4 Thread-1,5 Thread-1,6 Thread-1,7 Thread-1,8 Thread-1,9 MainThread,0 MainThread,1 MainThread,2上面的例子中,t1 線程調用了 join() 方法,并且沒有指定具體的 timeout 參數值。
這意味著如果程序想繼續往下執行,必須先執行完 t1 子線程。
總結
以上是生活随笔為你收集整理的如何改变 Python 中线程的执行顺序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python 中多线程共享全局变量的问题
- 下一篇: Python 中引入多个模块,包的概念