詳解python如何優(yōu)雅地關閉線程
在并發(fā)編程中,我們可能會創(chuàng)建新線程,并在其中運行任務,可能由于一些原因,決定停止該線程。例如:
- 不再需要線程任務的結果了。
- 應用程序正在關閉。
- 線程執(zhí)行可能已經(jīng)出現(xiàn)了異常
Threading 模塊的 Thread 類并沒有提供關閉線程的方法。如果不正確關閉子線程,可能遇到如下問題:
- 中止主線程后,子線程仍然在運行,成為僵尸進程
- 子線程打開的文件未能正確關閉,造成數(shù)據(jù)丟失
- 子線程打開的數(shù)據(jù)庫,未能提交更新,造成數(shù)據(jù)丟失
那么應該如何正確關閉線程呢?
1. Python 默認關閉線程的方式
線程對象創(chuàng)建后,調用start(方法運行, 執(zhí)行結束后,自動關閉。如下面的示例代碼:
#!/usr/bin/python # -*- coding: UTF-8 -*- import threading #導入threading 模塊 import time # 定義任務函數(shù) print_time def print_time ( threadName ,delay ): count = 0 while count < 5: time.sleep(delay) count += 1 print("%s: %s \n" % (threadName,time.ctime(time.time()))) # 定義任務函數(shù) print_cube def print_cube(num): #pring cube print("Cube:{} \n".format(num*num*num)) # 創(chuàng)建兩個線程 if __name__ == "__main__": # 創(chuàng)建兩個子線程 t1 = threading.Thread( target=print_cube,args=(10,)) t2 = threading.Thread( target=print_time,args=("Thread-2",4,)) #start threads t1.start() # start 后,子線程開始運行 t2.start() t1.join() #join 命令:讓主線程暫停運行,等待子線程運行結束。 t2.join() print("Done") # The statement is executed after sub threads done
2. 如何優(yōu)雅地關閉線程?
上節(jié)的例子,線程執(zhí)行時間短,很快可以結束,所以主線程可以等待其結束。但是如果子線程執(zhí)行的是1個耗時任務,如提供1個服務,或執(zhí)行1個Monitor 任務,子線程內可能存在永久循環(huán),這時子線程對象運行start()后,就一直處理運行狀態(tài)。
在WIndows系統(tǒng),如果應用程序直接退出,子線程自然也被強行中止,但子線程正在執(zhí)行的任務可能會受影響,如正在存取的文件可能正確關閉,造成數(shù)據(jù)丟失等。
在Linux系統(tǒng),如果應用程序直接退出,如使用kill命令殺死進程,未正確關閉的子線程可能仍在運行,成為僵尸進程。
那么如何優(yōu)雅地停止子線程呢?思路有兩個:
1) 通過設置全局狀態(tài)變量來關閉線程
2) 通過 threading.Event 對象來關閉線程
下面示例展示兩種方法的實現(xiàn)過程
2.1. 使用全局變量來關閉線程
實現(xiàn)步驟:
- 在線程內添加狀態(tài)變量
- 線程循環(huán)體內,檢測狀態(tài)變量,如果為False ,退出循環(huán)。
- 主線程需要關閉線程時,將子線程對象的狀態(tài)變量置為False即可。
2.1.1 關閉 thread類實現(xiàn)的線程
class CountdownTask: def __init__(self): self._running = True # 定義線程狀態(tài)變量 def terminate(self): self._running = False def run(self, n): # run方法的主循環(huán)條件加入對狀態(tài)變量的判斷 while self._running and n > 0: print('T-minus', n) n -= 1 time.sleep(5) print("thread is ended") c = CountdownTask() th = Thread(target = c.run, args =(10, )) th.start() # 對于耗時線程,沒必要再用join()方法了,注意主線程通常也需要有個監(jiān)控循環(huán) # … any code … # Signal termination q = input("please press any key to quit ") c.terminate()
2.1.2 關閉函數(shù)式線程
關閉函數(shù)式線程,可以用全局變量做狀態(tài)變量
import threading import time def run(): while True: print('thread running') global stop_threads if stop_threads: break stop_threads = False t1 = threading.Thread(target = run) t1.start() time.sleep(1) stop_threads = True t1.join() print('thread killed')
2.2. 使用 threading.Event 對象關閉子線程
2.2.1 Event 機制工作原理
Event 是線程間通信的一種方式。其作用相當于1個全局flag,主線程通過控制 event 對象狀態(tài),來協(xié)調子線程步調。
使用方式
- 主線程創(chuàng)建 event 對象,并將其做為參數(shù)傳給子線程
- 主線程可以用
set()
方法將event
對象置為true, 用clear()
方法將其置為false。 - 子線程循環(huán)體內,檢查 event 對象的值,如果為 True, 則退出循環(huán)。
- 子線程,可使用
event.wait()
將阻塞當前子進程,直至event 對象被置為true.
event 類的常用方法
- set() 設置 True
- clear() 設置 False,
- wait() 使進程等待,直到flag被改為true.
- is_set() 查詢 event 對象,如被設置為真,則返回True, 否則返回False.
if event.is_set(): # do something before end worker break
這種方式的優(yōu)點是,Event對象是線程安全的,而且速度更快,推薦使用這種方式關閉耗時線程。
2.2.2 完整代碼:
from time import sleep from threading import Thread from threading import Event # define task function def task(event): # execute a task in a loop for i in range(100): # block for a moment sleep(1) # check for stop if event.is_set(): # 在此添加退出前要做的工作,如保存文件等 break # report a message print('Worker thread running...') print('Worker is ended') # create the event event = Event() # create a thread thread = Thread(target=task, args=(event,)) # start the new thread thread.start() # block for a while sleep(3) # stop the worker thread print('Main stopping thread') event.set() # 這里是為了演示,實際開發(fā)時,主進程有事件循環(huán),耗時函數(shù)不需要調用join()方法 thread.join()
子線程執(zhí)行其任務循環(huán),它每次循環(huán)都會檢查event對象,該對象保持 false,就不會觸發(fā)線程停止。
當主線程調用event對象的 set() 方法后,在子線程循環(huán)體內,調用event對象is_set()方法,發(fā)現(xiàn)event 對象為True后, 立即退出任務循環(huán),結束運行。
到此這篇關于詳解python如何優(yōu)雅地關閉線程的文章就介紹到這了,更多相關python關閉線程內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
python計算Content-MD5并獲取文件的Content-MD5值方式
這篇文章主要介紹了python計算Content-MD5并獲取文件的Content-MD5值方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-04-04numpy中hstack vstack stack concatenate函數(shù)示例詳解
這篇文章主要為大家介紹了numpy中hstack vstack stack concatenate函數(shù)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02Python-re中search()函數(shù)的用法詳解(查找ip)
這篇文章主要介紹了Python-re中search()函數(shù)的用法-----查找ip,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03