<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何殺死一個(gè)Python線程

          共 5207字,需瀏覽 11分鐘

           ·

          2021-11-18 13:33

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

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

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

          A Threaded Example

          • 下面是一個(gè)簡(jiǎ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)行的,專門用來(lái)等待任何沒(méi)有被配置為守護(hù)線程的后臺(tái)線程結(jié)束,然后再把控制權(quán)真正交給操作系統(tǒng)。因此,該進(jìn)程在其主線程運(yùn)行時(shí)收到到了中斷信號(hào),并準(zhǔn)備退出。首先,它需要等待后臺(tái)線程運(yùn)行結(jié)束。但是,這個(gè)線程對(duì)中斷一無(wú)所知,這個(gè)線程只知道它需要在運(yùn)行結(jié)束前完成 30?次迭代。

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


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

          Daemon Threads

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

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

          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è)線程會(huì)發(fā)生什么呢?線程繼續(xù)運(yùn)行,就像什么都沒(méi)發(fā)生一樣,直到?Python?進(jìn)程終止并返回到操作系統(tǒng)。這時(shí),線程就不存在了。你可能認(rèn)為這實(shí)際上是一種殺死線程的方法,但要考慮到以這種方式殺死線程,你必須同時(shí)殺死進(jìn)程。


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

          Python Events

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

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

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

          exit_event = threading.Event()
          • Event?對(duì)象可以處于兩種狀態(tài)之一:?set?或?not 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í))

          • 其核心思路,就是在線程需要退出的時(shí)候設(shè)置事件。然后,線程需要經(jīng)常地檢查事件的狀態(tài)(通常是在循環(huán)中),并在發(fā)現(xiàn)事件已經(jīng)設(shè)置時(shí)處理自己的終止。對(duì)于上面顯示的示例,一個(gè)好的解決方案是添加一個(gè)捕獲?Ctrl-C?中斷的信號(hào)處理程序,而不是突然退出,只需設(shè)置事件并讓線程優(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)雅地處理的,以及線程能夠運(yùn)行在循環(huán)之后出現(xiàn)的代碼。如果當(dāng)線程需要在退出之前,關(guān)閉文件句柄或數(shù)據(jù)庫(kù)連接時(shí),這種方式就非常有用了。其能夠在線程退出之前,運(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)用,這將阻塞線程。如果在線程?sleep?時(shí)設(shè)置了退出事件,那么它就不能檢查事件的狀態(tài),因此在線程能夠退出之前會(huì)有一個(gè)小的延遲。在這種情況下,如果有?sleep,使用?wait()?方法將?sleep?與?event?對(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)樵诰€程停留在?wait()?調(diào)用的中間時(shí)設(shè)置了事件,那么等待將立即返回。


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

          Conclusion

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


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

          文章轉(zhuǎn)載:Python編程學(xué)習(xí)圈
          (版權(quán)歸原作者所有,侵刪)



          點(diǎn)擊下方“閱讀原文”查看更多

          瀏覽 41
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  99丝袜视频在线观看 | 操老逼网 | 国产成人毛片 | 免费无码一级A片大黄在线观看 | 日本黄色视频免费网站 |