2017-04-14 22 views
6

我知道__init __()會在你創建類newThread = MyThread(property)時自動調用,run()會被newthread.start()觸發。我正在尋找的是在線程終止之前自動調用的東西,所以我不必在每個return語句前明確呼叫self.cleanUp()。要做到這一點線程類中__init__的對面

class MyThread(Thread): 

    def __init__(self, property): 
     Thread.__init__(self) 
     self.property = property 

    def cleanUp(self): 
     # Clean up here 

    def run(self): 
     # Do some stuff 
     self.cleanUp() # Current work around 
     return 
+1

爲什麼你不使用'try:... finally:...'構造? – Leon

+4

雖然'__del __()'被認爲是相反的,但它在被調用時會有問題。我會看看上下文管理器,用在'with'子句中。 – AChampion

回答

6

一種方式是通過使Thread子也context manager。這將有效地使__exit__()您想要觸發的特殊方法。

下面顯示了我提議的內容。注意:我將property參數重命名爲傳遞構造函數,因爲property是內置Python的名稱。

from threading import Thread 
import time 

TEST_THREAD_EXCEPTION = False # change as desired 

class MyThread(Thread): 

    def __init__(self, attribute): 
     Thread.__init__(self) 
     self.attribute = attribute 

    def cleanup(self): 
     # Clean up here 
     print(' cleaning up after thread') 

    def run(self): 
     if TEST_THREAD_EXCEPTION: 
      raise RuntimeError('OOPS!') # force exception 
     print(' other thread now running...') 
     time.sleep(2) # Do something... 

    def __enter__(self): 
     try: 
      self.run() 
     except Exception as exc: 
      print('Error: {} exception raised by thread'.format(exc)) 
      raise # reraise the exception 
     return self 

    def __exit__(self, *args): 
     self.cleanup() 

print('main thread begins execution') 
with MyThread('hello') as thread: 
    print('doing other things in main thread while other thread is running') 
print('main thread continuing...') 

輸出:

main thread begins execution 
    other thread now running... 
doing other things in main thread while other thread is running 
    cleaning up after thread 
main thread continuing on... 

如果更改TEST_THREAD_EXCEPTIONTruecleanup()不會被稱爲因爲線程沒有成功運行,但你可以改變,如果你願意,但可能還需要確保它不會被調用兩次。下面是該代碼在這種情況下,上述的作用:

main thread begins execution 
Error: OOPS! exception raised by thread 
Traceback (most recent call last): 
    File "opposite_init.py", line 37, in <module> 
    with MyThread('hello') as thread: 
    File "opposite_init.py", line 27, in __enter__ 
    self.run() 
    File "opposite_init.py", line 21, in run 
    raise RuntimeError('OOPS!') # force exception 
RuntimeError: OOPS! 
+0

據我所知,你的例子並沒有在單獨的線程中實際運行任何東西 - 它只是從主線程調用MyThread的'run'方法。這是故意的嗎?這似乎不是OP所要求的。 – Dolda2000

+2

此外,'__exit__'僅在'__enter__'實際正常返回時調用。如果'run'方法會拋出一個異常,那麼'__exit__'不會被調用。 – Dolda2000

+0

@ Dolda2000:從意義上講,它只是一個例子(並且在代碼中提供了所有的OP) - 我理解爲「在線程終止之前自動調用的東西」。 – martineau

2

正如Python mailing list指出,__del__不應該被認爲是相反的,但你可以使用with語法,這是一個context manager

你不能確定一個對象的析構函數(__del__())會永遠調用 。如果要確保處理特定對象 ,則有一種方法是with-語法。

或者您也可以查看try ... finally子句,其中finally語句將始終運行。

class MyThread(Thread): 

    def __init__(self, property): 
     Thread.__init__(self) 
     self.property = property 

    def __enter__(self): 
     return self 

    def __exit__(self, exc_type, exc_value, traceback): 
     print('starting cleanup') 
     # Clean up here 

    def run(self): 
     # Do some stuff 
     return 

# not now you can call it like this: 
with MyThread("spam") as spam: 
    print("The thread is running") 
    # you can also do stuff here 

可以使用的try ... finally從句,像這樣:

class MyThread(Thread): 

    def __init__(self, property): 
     Thread.__init__(self) 
     self.property = property 

    def cleanUp(self): 
     # Clean up here 
     print('starting cleanup') 

    def run(self): 
     # Do some stuff 
     return 

try: 
    spam = MyThread('spam') 
    print('The thread is running') 
finally: 
    spam.cleanUp() 
+0

如果您希望爲垃圾郵件分配一個有用的值,您還需要定義'__enter__'。 – chepner

2

如果你想解決的問題是,你不希望將代碼添加到您的每一個run()方法來調用你的清理函數,那麼我會建議做一個自定義子類Thread這是你的。事情是這樣的,也許:

class CleanupThread(Thread): 
    def cleanup(self): 
     # Override this method in your subclasses to do cleanup 
     pass 

    def run2(self): 
     # Override this method in your subclasses instead of run() 
     pass 

    def run(self): 
     # Do *not* override this in your subclasses. Override run2() instead. 
     try: 
      self.run2() 
     finally: 
      self.cleanup() 

當然,你可以自由地重新命名run2的東西,是有道理的,你。

Python不提供這個內置的等價物,如果這就是你想要的。