Python之多線程退出與停止的一種實現(xiàn)思路
Python多線程退出與停止
在使用多線程的過程中,我們知道,python的線程是沒有stop/terminate方法的,也就是說它被啟動后,你無法再主動去退出它,除非主進(jìn)程退出了,注意,是主進(jìn)程,不是線程的父進(jìn)程.
一個比較合理的方式就是把原因需要放到threading.Thread的target中的線程函數(shù),改寫到一個繼承類中
下面是一個實現(xiàn)例子
import threading import time import os # 原本需要用來啟動的無線循環(huán)的函數(shù) def print_thread(): pid = os.getpid() counts = 0 while True: print(f'threading pid: {pid} ran: {counts:04d} s') counts += 1 time.sleep(1) # 把函數(shù)放到改寫到類的run方法中,便可以通過調(diào)用類方法,實現(xiàn)線程的終止 class StoppableThread(threading.Thread): def __init__(self, daemon=None): super(StoppableThread, self).__init__(daemon=daemon) self.__is_running = True self.daemon = daemon def terminate(self): self.__is_running = False def run(self): pid = os.getpid() counts = 0 while self.__is_running: print(f'threading running: {pid} ran: {counts:04d} s') counts += 1 time.sleep(1) def call_thread(): thread = StoppableThread() thread.daemon = True thread.start() pid = os.getpid() counts = 0 for i in range(5): print(f'0 call threading pid: {pid} ran: {counts:04d} s') counts += 2 time.sleep(2) # 主動把線程退出 thread.terminate() if __name__ == '__main__': call_thread() print(f'==========call_thread finish===========') counts = 0 for i in range(5): counts += 1 time.sleep(1) print(f'main thread:{counts:04d} s')
python多線程實現(xiàn)
用于線程實現(xiàn)的Python模塊
Python線程有時稱為輕量級進(jìn)程,因為線程比進(jìn)程占用的內(nèi)存少得多。 線程允許一次執(zhí)行多個任務(wù)。
在Python中,以下兩個模塊在一個程序中實現(xiàn)線程 -
_thread
模塊threading
模塊
這兩個模塊之間的主要區(qū)別在于_thread模塊將線程視為一個函數(shù),而threading模塊將每個線程視為一個對象并以面向?qū)ο蟮姆绞綄崿F(xiàn)它。
此外,_thread模塊在低級線程中有效并且比threading模塊具有更少的功能。
_thread 模塊
在Python的早期版本中,擁有thread模塊,但在相當(dāng)長的一段時間里它已被視為“已棄用”。 鼓勵用戶改用threading模塊。
因此,在Python 3中,thread模塊不再可用。 它已被重命名為_thread,用于Python3中的向后不兼容。
為了在_thread模塊的幫助下生成新的線程,我們需要調(diào)用它的start_new_thread方法。
這種方法的工作可以通過以下語法來理解 -
_thread.start_new_thread ( function, args[, kwargs] )
這里,
args
是一個參數(shù)的元組kwargs
是關(guān)鍵字參數(shù)的可選字典
如果想在不傳遞參數(shù)的情況下調(diào)用函數(shù),那么需要在args中使用一個空的參數(shù)元組。
此方法調(diào)用立即返回,子線程啟動,并調(diào)用與傳遞的列表(如果有的話)args的函數(shù)。 線程在函數(shù)返回時終止。
示例
以下是使用_thread模塊生成新線程的示例。在這里使用start_new_thread()方法
import _thread import time def print_time( threadName, delay): count = 0 while count < 5: time.sleep(delay) count += 1 print ("%s: %s" % ( threadName, time.ctime(time.time()) )) try: _thread.start_new_thread( print_time, ("Thread-1", 2, ) ) _thread.start_new_thread( print_time, ("Thread-2", 4, ) ) except: print ("Error: unable to start thread") while 1: pass
在_thread模塊的幫助下理解新線程的生成。
Thread-1: Mon Apr 23 10:03:33 2018
Thread-2: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:35 2018
Thread-1: Mon Apr 23 10:03:37 2018
Thread-2: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:39 2018
Thread-1: Mon Apr 23 10:03:41 2018
Thread-2: Mon Apr 23 10:03:43 2018
Thread-2: Mon Apr 23 10:03:47 2018
Thread-2: Mon Apr 23 10:03:51 2018
threading模塊
threading模塊以面向?qū)ο蟮姆绞綄崿F(xiàn),并將每個線程視為一個對象。
因此,它為線程提供了比_thread模塊更強大,更高層次的支持。
該模塊包含在Python 2.4中。
threading 模塊中的其他方法
threading模塊包含_thread模塊的所有方法,但它也提供了其他方法。
其他方法如下:
- threading.activeCount() - 此方法返回處于活動狀態(tài)的線程對象的數(shù)量
- threading.currentThread() - 此方法返回調(diào)用者線程控制中的線程對象數(shù)。
- threading.enumerate() - 此方法返回當(dāng)前活動的所有線程對象的列表。
為了實現(xiàn)線程,threading模塊具有提供以下方法的Thread類
- run() - run()方法是線程的入口點。
- start() - start()方法通過調(diào)用run方法來啟動線程。
- join([time]) - join()等待線程終止。
- isAlive() - isAlive()方法檢查線程是否仍在執(zhí)行。
- getName() - getName()方法返回線程的名稱。
- setName() - setName()方法設(shè)置線程的名稱。
如何使用 threading 模塊創(chuàng)建線程?
在本節(jié)中,我們將學(xué)習(xí)如何使用threading模塊創(chuàng)建線程。 按照以下步驟使用threading模塊創(chuàng)建一個新線程 -
第1步 - 在這一步中,需要定義Thread類的新子類。
第2步 - 然后為了添加額外的參數(shù),需要重寫__init __(self [,args])方法。
第3步 - 在這一步中,需要重寫run(self [,args])方法來實現(xiàn)線程在啟動時應(yīng)該執(zhí)行的操作。
現(xiàn)在,在創(chuàng)建新的Thread子類后,可以創(chuàng)建它的一個實例,然后通過調(diào)用start()來啟動一個新線程,start()又調(diào)用run()方法。
示例
下面這個例子演示如何使用threading模塊生成一個新的線程
import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("Starting " + self.name) print_time(self.name, self.counter, 5) print ("Exiting " + self.name) def print_time(threadName, delay, counter): while counter: if exitFlag: threadName.exit() time.sleep(delay) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) thread1.start() thread2.start() thread1.join() thread2.join() print ("Exiting Main Thread") Starting Thread-1 Starting Thread-2
執(zhí)行上面示例代碼,得到以下結(jié)果
Thread-1: Mon Apr 23 10:52:09 2018
Thread-1: Mon Apr 23 10:52:10 2018
Thread-2: Mon Apr 23 10:52:10 2018
Thread-1: Mon Apr 23 10:52:11 2018
Thread-1: Mon Apr 23 10:52:12 2018
Thread-2: Mon Apr 23 10:52:12 2018
Thread-1: Mon Apr 23 10:52:13 2018
Exiting Thread-1
Thread-2: Mon Apr 23 10:52:14 2018
Thread-2: Mon Apr 23 10:52:16 2018
Thread-2: Mon Apr 23 10:52:18 2018
Exiting Thread-2
Exiting Main Thread
帶有線程狀態(tài)的Python程序
有五種線程狀態(tài) - 新的,可運行,運行,等待和死亡。 在這五個中,我們將主要關(guān)注三個狀態(tài) - 運行,等待和死亡。
一個線程獲取處于運行狀態(tài)的資源,等待處于等待狀態(tài)的資源; 如果執(zhí)行和獲取的資源的最終版本處于死亡狀態(tài)。
下面的Python程序在start(),sleep()和join()方法的幫助下將分別顯示線程是如何進(jìn)入運行,等待和死亡狀態(tài)的
- 第1步 - 導(dǎo)入必要的模塊,threading和time
import threading import time
- 第2步 - 定義一個函數(shù),它將在創(chuàng)建線程時調(diào)用。
def thread_states(): print("Thread entered in running state")
- 第3步 - 使用time模塊的sleep()方法讓線程等待2秒鐘。
time.sleep(2)
- 第4步 - 現(xiàn)在,創(chuàng)建一個名為T1的線程,它接受上面定義的函數(shù)的參數(shù)。
T1 = threading.Thread(target=thread_states)
- 第5步 - 現(xiàn)在,使用start()函數(shù),可以開始啟動線程。 它會產(chǎn)生這個信息,這個信息是在定義函數(shù)時設(shè)定的。
T1.start()
- 第6步 - 現(xiàn)在,最后可以在完成執(zhí)行后使用join()方法終止線程。
T1.join()
在Python中啟動一個線程:
在python中,可以通過不同的方式啟動一個新的線程,但其中最簡單的一個就是將其定義為一個單一的函數(shù)。
在定義函數(shù)之后,可以將它作為新線程的目標(biāo)。線程對象等等。
執(zhí)行下面的Python代碼來理解函數(shù)的工作原理 -
import threading import time import random def Thread_execution(i): print("Execution of Thread {} started\n".format(i)) sleepTime = random.randint(1,4) time.sleep(sleepTime) print("Execution of Thread {} finished".format(i)) for i in range(4): thread = threading.Thread(target=Thread_execution, args=(i,)) thread.start() print("Active Threads:" , threading.enumerate()
執(zhí)行上面代碼,得到以下結(jié)果 -
Execution of Thread 0 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>]Execution of Thread 1 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>]Execution of Thread 2 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>]Execution of Thread 3 started
Active Threads:
[<_MainThread(MainThread, started 6040)>,
<HistorySavingThread(IPythonHistorySavingThread, started 5968)>,
<Thread(Thread-3576, started 3932)>,
<Thread(Thread-3577, started 3080)>,
<Thread(Thread-3578, started 2268)>,
<Thread(Thread-3579, started 4520)>]
Execution of Thread 0 finished
Execution of Thread 1 finished
Execution of Thread 2 finished
Execution of Thread 3 finished
在Python中啟動一個線程:
在python中,可以通過不同的方式啟動一個新的線程,但最簡單的就是將其定義為一個單一的函數(shù)。
在定義函數(shù)之后,可以將它作為新線程的目標(biāo)。線程對象等等。
執(zhí)行下面的Python代碼來理解函數(shù)的工作原理 -
import threading import time def nondaemonThread(): print("starting my thread") time.sleep(8) print("ending my thread") def daemonThread(): while True: print("Hello") time.sleep(2) if __name__ == '__main__': nondaemonThread = threading.Thread(target = nondaemonThread) daemonThread = threading.Thread(target = daemonThread) daemonThread.setDaemon(True) daemonThread.start() nondaemonThread.start(
在上面的代碼中,有兩個函數(shù),分別是- nondaemonThread()和daemonThread()。
第一個函數(shù)打印其狀態(tài)并在8秒后休眠,而deamonThread()函數(shù)每2秒無限期地打印出Hello。
我們可以通過以下輸出來了解nondaemon和daemon線程之間的區(qū)別 -
Hello
starting my thread
Hello
Hello
Hello
Hello
ending my thread
Hello
Hello
Hello
Hello
Hello
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Pygame與OpenCV聯(lián)合播放視頻并保證音畫同步
Pygame的Movie模塊已經(jīng)廢棄多年,本文主要介紹了Pygame與OpenCV聯(lián)合播放視頻并保證音畫同步,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12借助Paramiko通過Python實現(xiàn)linux遠(yuǎn)程登陸及sftp的操作
這篇文章主要介紹了借助Paramiko通過Python實現(xiàn)linux遠(yuǎn)程登陸及sftp,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03python實現(xiàn)鳶尾花三種聚類算法(K-means,AGNES,DBScan)
這篇文章主要介紹了python實現(xiàn)鳶尾花三種聚類算法(K-means,AGNES,DBScan),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06python判斷單向鏈表是否包括環(huán),若包含則計算環(huán)入口的節(jié)點實例分析
這篇文章主要介紹了python判斷單向鏈表是否包括環(huán),若包含則計算環(huán)入口的節(jié)點,結(jié)合實例形式分析了Python針對單向鏈表的遍歷、判斷相關(guān)算法原理與使用技巧,需要的朋友可以參考下2019-10-10windows10系統(tǒng)中安裝python3.x+scrapy教程
本文給大家主要介紹了在windows10系統(tǒng)中安裝python3以及scrapy框架的教程以及有可能會遇到的問題的解決辦法,希望大家能夠喜歡2016-11-11python xlsxwriter庫生成圖表的應(yīng)用示例
這篇文章主要介紹了python xlsxwriter庫生成圖表的應(yīng)用示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03