LoginSignup
18
10

More than 5 years have passed since last update.

Python concurrent.futuresのスレッド内の例外

Last updated at Posted at 2019-02-06

「スレッドがなぜか止まる」
しかも、Tracebackの表示もされない。
何か問題が起きているはずなのに、場所がわからない。
そんな経験はないでしょうか。

スレッド内の処理で例外が起きても既存の手段では拾えません。
正しい手続きで、例外を掬い上げる必要があります。

今回はpythonのconcurrent.futures.ThreadPoolExecutorについて記します。

下記は結果を戻さないルーチン。
func1を1秒ごとに表示します。
動かす分には問題ありません。

concurrent_test.py
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を置いてみます。

concurrent_test.py
def func1():
    while True:
        print("func1")
        raise Exception('dummy1')  ## New !
        time.sleep(1)

この場合、func1を一回出力するのみで停止します。
例外で停止しているにも関わらずTracebackなど、何も表示されません。
実行結果からはどこで止まっているか窺い知れないのです。
ではどうするのか。

future.result()を使います。
future.result()はスレッド内の処理の戻り値を得るためのものですが、
func1()に対して使ってみましょう。

concurrent_test.py
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()を置く事で、例外を得る事が出来ます。

18
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
10