2011-06-15 90 views
12

我正在嘗試編寫一個正在偵聽套接字,stdin和從文件描述符中讀取的unix客戶端程序。我將這些任務中的每一個分配給單獨的線程,並使用同步隊列和信號成功地與「主」應用程序通信。問題是,當我想關閉這些子線程時,它們都是輸入阻塞的。此外,線程無法在線程中註冊信號處理程序,因爲在Python中只允許執行的主線程執行此操作。優雅地終止Python線程

有什麼建議嗎?

+0

什麼樣的線程?有幾個庫提供了線程功能。 – Blender 2011-06-15 14:47:25

+0

不幸的是,當使用單獨的(POSIX)線程進行連接時,這只是冰山一角。請考慮使用非阻塞I/O(例如,使用Twisted)。 – 2011-06-15 14:53:19

+0

線程需要自行終止(或被告知這樣做)。你可以讓他們不被阻止,所以他們可以退出,如果信號? (polling或其他) – 2011-06-15 14:54:09

回答

7

沒有好辦法解決這個問題,特別是當線程阻塞時。

我有一個類似的問題(Python: How to terminate a blocking thread),我能阻止我的線程的唯一方法是關閉底層連接。這導致阻塞的線程升起和異常,然後讓我檢查停止標誌並關閉。

示例代碼:

class Example(object): 
    def __init__(self): 
     self.stop = threading.Event() 
     self.connection = Connection() 
     self.mythread = Thread(target=self.dowork) 
     self.mythread.start()  
    def dowork(self): 

     while(not self.stop.is_set()): 
      try: 
        blockingcall()   
      except CommunicationException: 
        pass 
    def terminate(): 
     self.stop.set() 
     self.connection.close() 
     self.mythread.join() 

另外一點需要注意的是常用阻塞操作一般提供了一個暫停。如果你有這個選擇,我會考慮使用它。我最後的評論是,你總是可以設置線程deamonic,

從是pydoc:

線程可以被標記爲「守護線程」。這個標誌的意義在於,只有守護進程線程退出時,整個Python程序纔會退出。初始值是從創建線程繼承的。該標誌可以通過守護進程屬性設置。

+2

請注意,觸摸另一個線程正在使用的filedescriptor是不安全的,它可能導致比其他線程中的異常更糟糕的事情。您應該真正使用非阻塞I/O。 – 2011-06-15 14:52:17

+0

+1我100%同意,但有些時候你不能......;( – Nix 2011-06-15 14:53:02

+0

不幸的是,雖然很少,當它是你正在操作的filedescriptors時(真正有問題的情況是阻止沒有這是一個不錯的解決方法:P) – 2011-06-15 14:54:54

0

見這些問題的答案:

Python SocketServer
How to exit a multithreaded program?

基本上,通過使用select()有超時檢查套接字的可讀性recv()不阻止,並查詢一個退出標誌時select()超時。

+0

這是一個不錯的解決方案,但我希望有一個更有說服力的方法來實現這一點,而不必經常循環。這也會導致延遲,只要我真的想殺死線程while它等待選擇超時。 – George 2011-06-15 15:15:05

+0

這就是線程的世界...... – theorifice 2016-10-25 12:05:19

1

此外,線程不能註冊信號處理程序

信號殺線程是潛在的恐怖,特別是在C,尤其是如果分配存儲器中作爲螺紋的一部分,因爲它不會被釋放當特定的線程死亡時(因爲它屬於進程的堆)。 C中沒有垃圾回收,所以如果該指針超出範圍,它就超出了範圍,內存仍然分配。所以只需要小心一點 - 只有在C中這樣做才能殺死所有線程並結束進程,從而將內存交還給操作系統 - 例如,從線程池中添加和刪除線程會給你一個內存泄漏。

問題是,當我想關閉這些子線程時,它們都是輸入阻塞。

有趣的是,最近我一直在用同樣的東西打架。解決方法是從字面上不會在沒有超時的情況下發出阻止呼叫。因此,例如,你想要什麼理想的是:

def threadfunc(running): 

    while running: 
     blockingcall(timeout=1) 

其中運行從控制線程過去了 - 我從來沒有用過線程,但我已經使用多以及與此你真正需要傳遞一個Event()對象,檢查is_set()。但是你要求設計模式,這是基本的想法。

然後,當你想這個線程結束,我們可以運行:

running.clear() 
mythread.join() 

,然後你的主線程應該讓你的客戶的線程來處理它的最後一次通話,並返回,整個程序更好地摺疊起來。

如果您在沒有超時的情況下阻止呼叫,您會做什麼?如果需要,可以使用異步選項,並進入休眠狀態(如調用任何方法來暫停線程一段時間,以免不旋轉)。沒有別的辦法。