0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

在Python中用于終止線(xiàn)程的兩個(gè)選項(xiàng)

馬哥Linux運(yùn)維 ? 來(lái)源:Escape ? 作者:Escape ? 2021-11-17 10:02 ? 次閱讀

我經(jīng)常被問(wèn)到如何殺死一個(gè)后臺(tái)線(xiàn)程,這個(gè)問(wèn)題的答案讓很多人不開(kāi)心: 線(xiàn)程是殺不死的。在本文中,我將向您展示Python中用于終止線(xiàn)程的兩個(gè)選項(xiàng)。

如果我們是一個(gè)好奇寶寶的話(huà),可能會(huì)遇到這樣一個(gè)問(wèn)題,就是:如何殺死一個(gè)Python的后臺(tái)線(xiàn)程呢?我們可能?chē)L試解決這個(gè)問(wèn)題,卻發(fā)現(xiàn)線(xiàn)程是殺不死的。而本文中將展示,在Python中用于終止線(xiàn)程的兩個(gè)方式。

1. 線(xiàn)程無(wú)法結(jié)束

A Threaded Example

  • 下面是一個(gè)簡(jiǎn)單的,多線(xiàn)程的示例代碼。

import randomimport threadingimport time
def bg_thread():    for i in range(1, 30):        print(f'{i} of 30 iterations...')        time.sleep(random.random())  # do some work...    print(f'{i} iterations completed before exiting.')
th = threading.Thread(target=bg_thread)th.start()th.join()
  • 使用下面命令來(lái)運(yùn)行程序,在下面的程序運(yùn)行中,當(dāng)跑到第7次迭代時(shí),按下Ctrl-C來(lái)中斷程序,發(fā)現(xiàn)后臺(tái)運(yùn)行的程序并沒(méi)有終止掉。而在第13次迭代時(shí),再次按下Ctrl-C來(lái)中斷程序,發(fā)現(xiàn)程序真的退出了。

$ python thread.py1 of 30 iterations...2 of 30 iterations...3 of 30 iterations...4 of 30 iterations...5 of 30 iterations...6 of 30 iterations...7 of 30 iterations...^CTraceback (most recent call last):  File "thread.py", line 14, in     th.join()  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1011, in join    self._wait_for_tstate_lock()  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1027, in _wait_for_tstate_lock    elif lock.acquire(block, timeout):KeyboardInterrupt8 of 30 iterations...9 of 30 iterations...10 of 30 iterations...11 of 30 iterations...12 of 30 iterations...13 of 30 iterations...^CException ignored in: Traceback (most recent call last):  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1388, in _shutdown    lock.acquire()KeyboardInterrupt:
  • 這很奇怪,不是嗎?究其原因是,Python 有一些邏輯是會(huì)在進(jìn)程退出前運(yùn)行的,專(zhuān)門(mén)用來(lái)等待任何沒(méi)有被配置為守護(hù)線(xiàn)程的后臺(tái)線(xiàn)程結(jié)束,然后再把控制權(quán)真正交給操作系統(tǒng)。因此,該進(jìn)程在其主線(xiàn)程運(yùn)行時(shí)收到到了中斷信號(hào),并準(zhǔn)備退出。首先,它需要等待后臺(tái)線(xiàn)程運(yùn)行結(jié)束。但是,這個(gè)線(xiàn)程對(duì)中斷一無(wú)所知,這個(gè)線(xiàn)程只知道它需要在運(yùn)行結(jié)束前完成 30次迭代。

  • Python 在退出過(guò)程中使用的等待機(jī)制有一個(gè)規(guī)定,當(dāng)收到第二個(gè)中斷信號(hào)時(shí),就會(huì)中止。這就是為什么第二個(gè) Ctrl-C 會(huì)立即結(jié)束進(jìn)程。所以我們看到了,線(xiàn)程是不能被殺死!在下面的章節(jié)中,將向展示 Python 中的兩個(gè)方式,來(lái)使線(xiàn)程及時(shí)結(jié)束。


2. 使用守護(hù)進(jìn)程

Daemon Threads

  • 在上面提到過(guò),在Python退出之前,它會(huì)等待任何非守護(hù)線(xiàn)程的線(xiàn)程。而守護(hù)線(xiàn)程就是,一個(gè)不會(huì)阻止Python解釋器退出的線(xiàn)程。

  • 如何使一個(gè)線(xiàn)程成為一個(gè)守護(hù)線(xiàn)程?所有的線(xiàn)程對(duì)象都有一個(gè)daemon屬性,可以在啟動(dòng)線(xiàn)程之前將這個(gè)屬性設(shè)置為True,然后該線(xiàn)程就會(huì)被視為一個(gè)守護(hù)線(xiàn)程。下面是上面的示例應(yīng)用程序,修改后守護(hù)線(xiàn)程版本:

import randomimport threadingimport time
def bg_thread():    for i in range(1, 30):        print(f'{i} of 30 iterations...')        time.sleep(random.random())  # do some work...    print(f'{i} iterations completed before exiting.')
th = threading.Thread(target=bg_thread)th.daemon = Trueth.start()th.join()
  • 再次運(yùn)行它,并嘗試中斷它,發(fā)現(xiàn)第一個(gè)執(zhí)行Ctrl-C后進(jìn)程立即就退出了。

~ $ python x.py1 of 30 iterations...2 of 30 iterations...3 of 30 iterations...4 of 30 iterations...5 of 30 iterations...6 of 30 iterations...^CTraceback (most recent call last):  File "thread.py", line 15, in     th.join()  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1011, in join    self._wait_for_tstate_lock()  File "/Users/mgrinberg/.pyenv/versions/3.8.6/lib/python3.8/threading.py", line 1027, in _wait_for_tstate_lock    elif lock.acquire(block, timeout):KeyboardInterrupt
  • 那么這個(gè)線(xiàn)程會(huì)發(fā)生什么呢?線(xiàn)程繼續(xù)運(yùn)行,就像什么都沒(méi)發(fā)生一樣,直到Python進(jìn)程終止并返回到操作系統(tǒng)。這時(shí),線(xiàn)程就不存在了。你可能認(rèn)為這實(shí)際上是一種殺死線(xiàn)程的方法,但要考慮到以這種方式殺死線(xiàn)程,你必須同時(shí)殺死進(jìn)程。


3. 使用事件對(duì)象

Python Events

  • 使用守護(hù)線(xiàn)程,是一種避免在多線(xiàn)程程序中處理意外中斷的簡(jiǎn)單方法,但這是一種只在進(jìn)程退出的特殊情況下才有效的技巧。不幸的是,有些時(shí)候,一個(gè)應(yīng)用程序可能想結(jié)束一個(gè)線(xiàn)程而不必殺死自己。另外,有些線(xiàn)程可能需要在退出前執(zhí)行清理工作,而守護(hù)線(xiàn)程則不允許這樣操作。

  • 那么,還有什么其他選擇呢?既然不可能強(qiáng)制線(xiàn)程結(jié)束,那么唯一的選擇就是給它添加邏輯,讓它在被要求退出時(shí)自愿退出。有多種方法都可以解決上述問(wèn)題,但我特別喜歡的一種方法,就是使用一個(gè)Event對(duì)象。

Event類(lèi)是由Python標(biāo)準(zhǔn)庫(kù)的線(xiàn)程模塊提供,你可以通過(guò)實(shí)例化類(lèi)來(lái)創(chuàng)建一個(gè)事件對(duì)象,就像下面這個(gè)樣子:

exit_event = threading.Event()
  • Event對(duì)象可以處于兩種狀態(tài)之一:setnot set。當(dāng)我們實(shí)例化創(chuàng)建之后,默認(rèn)事件并沒(méi)有被設(shè)置。

    • 若要將事件狀態(tài)更改為set,則可以調(diào)用set()方法;

    • 要查明是否設(shè)置了事件,使用is_set()方法,設(shè)置了則返回True;

    • 還可以使用wait()方法等待事件,等待操作阻塞直到設(shè)置事件(可以設(shè)置超時(shí))

  • 其核心思路,就是在線(xiàn)程需要退出的時(shí)候設(shè)置事件。然后,線(xiàn)程需要經(jīng)常地檢查事件的狀態(tài)(通常是在循環(huán)中),并在發(fā)現(xiàn)事件已經(jīng)設(shè)置時(shí)處理自己的終止。對(duì)于上面顯示的示例,一個(gè)好的解決方案是添加一個(gè)捕獲Ctrl-C中斷的信號(hào)處理程序,而不是突然退出,只需設(shè)置事件并讓線(xiàn)程優(yōu)雅地結(jié)束。

import randomimport signalimport threadingimport time
exit_event = threading.Event()
def bg_thread():    for i in range(1, 30):        print(f'{i} of 30 iterations...')        time.sleep(random.random())  # do some work...        if exit_event.is_set():            break    print(f'{i} iterations completed before exiting.')
def signal_handler(signum, frame):    exit_event.set()
signal.signal(signal.SIGINT, signal_handler)th = threading.Thread(target=bg_thread)th.start()th.join()
  • 如果你嘗試中斷這個(gè)版本的應(yīng)用程序,一切看起來(lái)都會(huì)更好:

$ python thread.py1 of 30 iterations...2 of 30 iterations...3 of 30 iterations...4 of 30 iterations...5 of 30 iterations...6 of 30 iterations...7 of 30 iterations...^C7 iterations completed before exiting.
  • 需要注意的是,中斷是如何被優(yōu)雅地處理的,以及線(xiàn)程能夠運(yùn)行在循環(huán)之后出現(xiàn)的代碼。如果當(dāng)線(xiàn)程需要在退出之前,關(guān)閉文件句柄或數(shù)據(jù)庫(kù)連接時(shí),這種方式就非常有用了。其能夠在線(xiàn)程退出之前,運(yùn)行清理代碼有時(shí)是必要的,以避免資源泄漏。我在上面提到過(guò),event對(duì)象也是可以等待的:

for i in range(1, 30):    print(f'{i} of 30 iterations...')    time.sleep(random.random())
    if exit_event.is_set():        break
  • 在每個(gè)迭代中,都有一個(gè)對(duì)time.sleep()的調(diào)用,這將阻塞線(xiàn)程。如果在線(xiàn)程sleep時(shí)設(shè)置了退出事件,那么它就不能檢查事件的狀態(tài),因此在線(xiàn)程能夠退出之前會(huì)有一個(gè)小的延遲。在這種情況下,如果有sleep,使用wait()方法將sleepevent對(duì)象的檢查結(jié)合起來(lái)會(huì)更有效:

   for i in range(1, 30):        print(f'{i} of 30 iterations...')        if exit_event.wait(timeout=random.random()):            break

  • 這個(gè)解決方案有效地為提供了一個(gè)可中斷的sleep,因?yàn)樵诰€(xiàn)程停留在wait()調(diào)用的中間時(shí)設(shè)置了事件,那么等待將立即返回。


4. 總結(jié)陳述說(shuō)明

Conclusion

  • 你知道Python中的event對(duì)象嗎?它們是比較簡(jiǎn)單的同步原語(yǔ)之一,不僅可以用作退出信號(hào),而且在線(xiàn)程需要等待某些外部條件發(fā)生的許多其他情況下也可以使用。

原文鏈接:https://www.escapelife.site/posts/558f583c.html

責(zé)任編輯:haq
聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • python
    +關(guān)注

    關(guān)注

    56

    文章

    4798

    瀏覽量

    84798
  • 線(xiàn)程
    +關(guān)注

    關(guān)注

    0

    文章

    505

    瀏覽量

    19705

原文標(biāo)題:如何殺死一個(gè)Python線(xiàn)程

文章出處:【微信號(hào):magedu-Linux,微信公眾號(hào):馬哥Linux運(yùn)維】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Python中多線(xiàn)程和多進(jìn)程的區(qū)別

    Python作為一種高級(jí)編程語(yǔ)言,提供了多種并發(fā)編程的方式,其中多線(xiàn)程與多進(jìn)程是最常見(jiàn)的種方式之一。本文中,我們將探討Python中多
    的頭像 發(fā)表于 10-23 11:48 ?415次閱讀
    <b class='flag-5'>Python</b>中多<b class='flag-5'>線(xiàn)程</b>和多進(jìn)程的區(qū)別

    ad如何設(shè)置兩個(gè)元器件的距離

    Altium Designer(簡(jiǎn)稱(chēng)AD)中設(shè)置兩個(gè)元器件之間的距離,主要是通過(guò)設(shè)置元器件間的安全間距(Clearance)規(guī)則來(lái)實(shí)現(xiàn)的。這個(gè)規(guī)則定義了元器件之間、元器件與走線(xiàn)之間以及其他設(shè)計(jì)元素
    的頭像 發(fā)表于 09-02 15:31 ?7574次閱讀

    功放機(jī)AB兩個(gè)聲道輸出怎么接

    功放機(jī)AB兩個(gè)聲道輸出的接線(xiàn)方式,主要取決于您想要實(shí)現(xiàn)的音頻效果以及音箱的配置。以下將詳細(xì)介紹幾種常見(jiàn)的接線(xiàn)方式,以及它們各自的特點(diǎn)和適用場(chǎng)景。 一、基礎(chǔ)接線(xiàn)方式 大多數(shù)情況下,功放機(jī)的AB兩個(gè)
    的頭像 發(fā)表于 08-23 10:40 ?3125次閱讀

    觸發(fā)器的兩個(gè)穩(wěn)定狀態(tài)分別是什么

    觸發(fā)器作為數(shù)字電路中的基本邏輯單元,具有兩個(gè)穩(wěn)定狀態(tài),這兩個(gè)狀態(tài)通常用于表示二進(jìn)制數(shù)碼中的0和1。
    的頭像 發(fā)表于 08-12 11:01 ?1401次閱讀

    雙穩(wěn)態(tài)電路的兩個(gè)穩(wěn)定狀態(tài)是什么

    雙穩(wěn)態(tài)電路是一種具有兩個(gè)穩(wěn)定狀態(tài)的電子電路,廣泛應(yīng)用于數(shù)字電路、通信系統(tǒng)、存儲(chǔ)器等領(lǐng)域。 雙穩(wěn)態(tài)電路的基本概念 雙穩(wěn)態(tài)電路是一種具有兩個(gè)穩(wěn)定狀態(tài)的電路,即在沒(méi)有外部輸入信號(hào)的情況下,電路可以保持
    的頭像 發(fā)表于 08-11 15:00 ?1548次閱讀

    雙穩(wěn)態(tài)觸發(fā)器的兩個(gè)基本性質(zhì)是什么

    雙穩(wěn)態(tài)觸發(fā)器(Bistable Trigger)是一種具有兩個(gè)穩(wěn)定狀態(tài)的邏輯電路,廣泛應(yīng)用于數(shù)字電路設(shè)計(jì)中。它具有兩個(gè)基本性質(zhì):記憶性和切換性。 一、雙穩(wěn)態(tài)觸發(fā)器的基本概念 1.1 雙穩(wěn)態(tài)觸發(fā)器
    的頭像 發(fā)表于 08-11 10:08 ?740次閱讀

    兩個(gè)PLC之間如何交互信號(hào)

    工業(yè)自動(dòng)化系統(tǒng)中,PLC(Programmable Logic Controller,可編程邏輯控制器)是核心的控制設(shè)備。許多復(fù)雜的應(yīng)用場(chǎng)景中,需要兩個(gè)或多個(gè)PLC之間進(jìn)行信號(hào)交互,以實(shí)現(xiàn)更高
    的頭像 發(fā)表于 06-14 16:57 ?4538次閱讀

    鴻蒙開(kāi)發(fā):【線(xiàn)程模型】

    管理其他線(xiàn)程的ArkTS引擎實(shí)例,例如使用TaskPool(任務(wù)池)創(chuàng)建任務(wù)或取消任務(wù)、啟動(dòng)和終止Worker線(xiàn)程。
    的頭像 發(fā)表于 06-13 16:38 ?425次閱讀
    鴻蒙開(kāi)發(fā):【<b class='flag-5'>線(xiàn)程</b>模型】

    兩個(gè)銅片可以形成原電池嗎

    兩個(gè)銅片本身不能形成原電池,因?yàn)樵姵氐墓ぷ髟硪蕾?lài)于兩個(gè)不同電位的電極材料之間的氧化還原反應(yīng)。
    的頭像 發(fā)表于 05-21 16:23 ?1017次閱讀

    為什么交流電橋中至少需要兩個(gè)可調(diào)參數(shù)?

    交流電橋的測(cè)量中,至少需要兩個(gè)可調(diào)參數(shù)的原因與電橋的工作原理、測(cè)量的準(zhǔn)確性以及校準(zhǔn)過(guò)程有關(guān)。
    的頭像 發(fā)表于 05-15 17:49 ?1910次閱讀

    原電池中的兩個(gè)電極能是相同的嗎?

    原電池的設(shè)計(jì)和運(yùn)作中,兩個(gè)電極是否可以相同,這取決于電池的類(lèi)型和所需的電化學(xué)反應(yīng)。
    的頭像 發(fā)表于 04-26 17:32 ?2517次閱讀

    python中5種線(xiàn)程鎖盤(pán)點(diǎn)

    線(xiàn)程安全是多線(xiàn)程或多進(jìn)程編程中的一個(gè)概念,擁有共享數(shù)據(jù)的多條線(xiàn)程并行執(zhí)行的程序中,線(xiàn)程安全的代
    發(fā)表于 03-07 11:08 ?1617次閱讀
    <b class='flag-5'>python</b>中5種<b class='flag-5'>線(xiàn)程</b>鎖盤(pán)點(diǎn)

    arcgis中如何關(guān)聯(lián)兩個(gè)屬性表

    ArcGIS中,關(guān)聯(lián)兩個(gè)屬性表是一個(gè)重要的操作,可以通過(guò)此操作將兩個(gè)表中的數(shù)據(jù)關(guān)聯(lián)起來(lái),以便進(jìn)行分析和查詢(xún)。下面是詳細(xì)介紹如何在ArcGIS中實(shí)現(xiàn)屬性表的關(guān)聯(lián)。 首先,我們需要明確
    的頭像 發(fā)表于 02-25 11:01 ?4309次閱讀

    求助,關(guān)于FX3的兩個(gè)使用問(wèn)題求解

    如題,咨詢(xún)FX3的兩個(gè)使用問(wèn)題: 1,F1F模式下,燒寫(xiě)成功后,WIN10無(wú)法識(shí)別設(shè)備,切換到0F1,燒寫(xiě)成功后,WIN10同樣無(wú)法識(shí)別設(shè)備,所以現(xiàn)在我的板子只能使用USB引導(dǎo)了,燒寫(xiě)到RAM可以
    發(fā)表于 02-22 07:16

    兩個(gè)電位器地控制一個(gè)變頻器,如何接線(xiàn)?

    是一種可調(diào)節(jié)電阻器,可以通過(guò)改變電阻值來(lái)控制電流或電壓。變頻器是一種能夠調(diào)整電機(jī)轉(zhuǎn)速的設(shè)備,讓電機(jī)不同的頻率下工作。 3. 確定電位器的安裝位置。在這個(gè)場(chǎng)景中,我們需要兩個(gè)電位器來(lái)控制一個(gè)變頻器,所以需要將
    的頭像 發(fā)表于 02-05 10:13 ?5442次閱讀