「スレッドがなぜか止まる」
しかも、Tracebackの表示もされない。
何か問題が起きているはずなのに、場所がわからない。
そんな経験はないでしょうか。
スレッド内の処理で例外が起きても既存の手段では拾えません。
正しい手続きで、例外を掬い上げる必要があります。
今回はpythonのconcurrent.futures.ThreadPoolExecutorについて記します。
下記は結果を戻さないルーチン。
func1
を1秒ごとに表示します。
動かす分には問題ありません。
import concurrent.futures
import time
def func1():
while True:
print("func1")
time.sleep(1)
if __name__ == "__main__":
executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)
func1_future = executor.submit(func1)
もし、func1()の中で例外が起きたらどうなるでしょうか。
意図的にraiseを置いてみます。
def func1():
while True:
print("func1")
raise Exception('dummy1') ## New !
time.sleep(1)
この場合、func1
を一回出力するのみで停止します。
例外で停止しているにも関わらずTracebackなど、何も表示されません。
実行結果からはどこで止まっているか窺い知れないのです。
ではどうするのか。
**future.result()**を使います。
future.result()はスレッド内の処理の戻り値を得るためのものですが、
func1()に対して使ってみましょう。
if __name__ == "__main__":
executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)
func1_future = executor.submit(func1)
func1_future.result() ## New!!
実行結果
func1
Traceback (most recent call last):
File "concurrent_test.py", line 34, in <module>
func1_future.result()
File "/Users/mms/.pyenv/versions/3.4.2/lib/python3.4/concurrent/futures/_base.py", line 402, in result
return self.__get_result()
File "/Users/mms/.pyenv/versions/3.4.2/lib/python3.4/concurrent/futures/_base.py", line 354, in __get_result
raise self._exception
File "/Users/mms/.pyenv/versions/3.4.2/lib/python3.4/concurrent/futures/thread.py", line 54, in run
result = self.fn(*self.args, **self.kwargs)
File "concurrent_test.py", line 7, in func1
raise Exception('dummy1')
Exception: dummy1
無事、Exception: dummy1
の情報を得ることが出来ました。
future.result()がException('dummy1')のraise を肩代わりするからです。
※他、future.exception()を利用する事でも例外の情報が得られます。この場合raiseではなくexception()の戻り値として渡されます。
まとめ
戻り値として結果を求めない処理等は、future.result()を省かれるかもしれません。
future.result()かfuture.exception()を置く事で、例外を得る事が出来ます。