進擊のpython
並發編程——線程方法
開啟了線程之后,就要學習一下對應的方法
本小節對線程的一些方法進行簡單的理解:
1.Thread的join方法
2.Thread的terminate與is_alive
Thread的join方法
join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)
timeout是可選的超時時間
首先,系統在運行的過程中可能會出現這樣的情況:
1.主線程和子線程彼此獨立,在都完成運行之后,由系統進行統一回收
2.主線程等子進程運行完畢之后再執行
第一種情況好說,第二種情況就需要有一種機制能夠讓主線程檢測子進程是否運行完畢
在子線程執行完畢后才繼續執行,否則一直在原地阻塞,這就是join方法的作用
from threading import Thread
def work():
print('我是子線程')
if __name__ == '__main__':
n = 100
t = Thread(target=work)
t.start()
# t.join()
print('我是主線程')
在沒有利用join方法的時候,執行順序是這樣的
我是子線程我是主線程
# 因為太快了,就打在一行了
利用join之后,執行順序就變成
我是子線程
我是主線程
可以看到,主線程的代碼在等待子進程代碼運行結束才開始執行
那是變成串行了嗎???
import time
from threading import Thread
def work(s):
time.sleep(s)
print('我是子線程')
if __name__ == '__main__':
n = 100
t = Thread(target=work, args=(1,))
t1 = Thread(target=work, args=(1,))
t2 = Thread(target=work, args=(1,))
start_time = time.time()
t.start()
t1.start()
t2.start()
t.join()
t1.join()
t2.join()
print('我是主線程', time.time() - start_time)
如果將join理解成串行,那么,子程序的執行時間應該是1+1+1 = 3s多
先執行t,睡1s,再執行t1,睡1s,再執行t2,睡1s
那我們來看一下執行結果:
我是子線程
我是子線程
我是子線程
我是主線程 1.002457857131958
時間是1s多,說明不是串行,依舊是並發執行
在開始介紹過,join是主線程等待子線程運行結束,再運行
t t1 t2 都是子線程,彼此不需要等待,是並發執行的狀態
所以子線程互相都是共享時間的,都是在執行的
而當子線程中1s的執行完了,也就意味着所有的子線程執行完畢了
才會執行主線程,所以子線程的執行時間只有1s多
上述的代碼也可以優化一下:
import time
from threading import Thread
def work(s):
time.sleep(s)
print('我是子線程')
if __name__ == '__main__':
n = 100
t = Thread(target=work, args=(1,))
t1 = Thread(target=work, args=(1,))
t2 = Thread(target=work, args=(1,))
l = [t,t1,t2]
start_time = time.time()
for i in l:
i.start()
for i in l:
i.join()
print('我是主線程', time.time() - start_time)
Thread的其他方法
- is_alive 線程是否存活
- getName 返回線程名
- setName 設置線程名
- threading.currentThread 返回當前的線程變量
- threading.enumerate 返回一個包含正在運行的線程的list,正在運行指線程啟動后、結束前,不包括啟動前和終止后的線程
- threading.activeCount 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果
from threading import Thread
import threading
def work():
import time
time.sleep(3)
print(threading.current_thread().getName())
if __name__ == '__main__':
# 在主進程下開啟線程
t = Thread(target=work)
t.start()
print(threading.current_thread().getName())
print(threading.current_thread()) # 主線程
print(threading.enumerate()) # 連同主線程在內有兩個運行的線程
print(threading.active_count())
print('主線程/主進程')
MainThread
<_MainThread(MainThread, started 8504)>
[<_MainThread(MainThread, started 8504)>, <Thread(Thread-1, started 4944)>]
2
主線程/主進程
Thread-1
守護線程
無論是進程還是線程,都遵循:守護xxx會等待主xxx運行完畢后被銷毀
需要強調的是:運行完畢並非終止運行
對主進程來說,運行完畢指的是主進程代碼運行完畢
對主線程來說,運行完畢指的是主線程所在的進程內所有非守護線程統統運行完畢,主線程才算運行完畢
怎么理解呢?
主進程在其代碼結束后就已經算運行完畢了(守護進程在此時就被回收),然后主進程會一直等非守護的子進程都運行完畢后回收子進程的資源(否則會產生僵屍進程),才會結束
主線程在其他非守護線程運行完畢后才算運行完畢(守護線程在此時就被回收)。因為主線程的結束意味着進程的結束,進程整體的資源都將被回收,而進程必須保證非守護線程都運行完畢后才能結束。
from threading import Thread
import time
def sayhi(name):
time.sleep(2)
print('%s say hello' % name)
if __name__ == '__main__':
t = Thread(target=sayhi, args=('egon',))
t.setDaemon(True) # 必須在t.start()之前設置
t.start()
print('主線程')
print(t.is_alive())
主線程
True
主線程代碼執行完畢,也要等子線程執行完,才算執行完