起因
今天在寫東西的時候,用到了多線程。遇到了個問題:
子線程的異常,在父線程中無法捕獲。
解決
問題代碼
問題代碼示例代碼如下:
import threading
class SampleThread(threading.Thread):
def run(self):
raise Exception('An error occured here.')
def main():
try:
thread_obj = SampleThread()
thread_obj.start()
except Exception:
print 'catch that'
if __name__ == '__main__':
main()
運行輸出結果如下:
Exception in thread Thread-1:
Traceback (most recent call last):
File "/path/threading.py", line 810, in __bootstrap_inner
self.run()
File "/PycharmProjects/example/threading_example.py", line 15, in run
raise Exception('An error occured here.')
Exception: An error occured here.
解決辦法
通過查看資料:http://stackoverflow.com/questions/2829329/catch-a-threads-exception-in-the-caller-thread-in-python
出現上述問題是因為:執行到 thread_obj.start()
時,父線程就會立即返回結果。然后,生成的子線程在自己獨立的上下文中執行,並使用自己的堆棧。子線程發生的任何異常都是在子線程的上下文中,並且它在它自己的堆棧中(獨立於父線程)。
所以,解決辦法是:將這些信息傳遞給父線程。代碼如下:
import sys
import threading
import Queue
class ExcThread(threading.Thread):
def __init__(self, bucket):
threading.Thread.__init__(self)
self.bucket = bucket
def run(self):
try:
raise Exception('An error occured here.')
except Exception:
self.bucket.put(sys.exc_info())
def main():
bucket = Queue.Queue()
thread_obj = ExcThread(bucket)
thread_obj.start()
while True:
try:
exc = bucket.get(block=False)
except Queue.Empty:
pass
else:
exc_type, exc_obj, exc_trace = exc
# deal with the exception
print exc_type, exc_obj
print exc_trace
thread_obj.join(0.1)
if thread_obj.isAlive():
continue
else:
break
if __name__ == '__main__':
import sys
import threading
import Queue
class ExcThread(threading.Thread):
def __init__(self, bucket):
super(ExcThread, self).__init__()
self.bucket = bucket
def run(self):
try:
raise Exception('An error occured here.')
except Exception:
# 異常信息元祖放入隊列傳遞給父進程
self.bucket.put(sys.exc_info())
def main():
bucket = Queue.Queue()
thread_obj = ExcThread(bucket)
thread_obj.start()
# 循環獲取子線程的異常信息
while 1:
try:
exc = bucket.get(block=False)
except Queue.Empty:
pass
else:
exc_type, exc_obj, exc_trace = exc
# deal with the exception
print exc_type, exc_obj
print exc_trace
# 防止阻塞
thread_obj.join(0.1)
if thread_obj.isAlive():
continue
else:
break
if __name__ == '__main__':
main()