LoginSignup
3
5

More than 3 years have passed since last update.

[python, multiprocessing] multiprocessing使用時の例外に対する挙動

Last updated at Posted at 2020-02-21

Pythonで並列処理などのためにmultiprocessingを使うが、multiprocessingで子プロセスを生成した時の挙動は、通常の関数呼び出しの挙動と異なる点がいくつかある。

準備

今回は簡単のため関数sleep_bug()を用いる。これは実行中に途中でわざとエラーを発生させるために, i==5の時に1/0を実行しエラーを発生するようにした関数である。

import time

def sleep_bug():
    for i in range(10):
        print('sleeping %d' % i)
        if i == 5:
            1/0
        time.sleep(1)
    return i

sleep_bug()

# output
'''
sleeping 0
sleeping 1
sleeping 2
sleeping 3
sleeping 4
sleeping 5
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-44-d9f02a4cf7f3> in <module>
----> 1 sleep_bug()

<ipython-input-41-26bb27998e63> in sleep_bug()
     12         print('sleeping %d' % i)
     13         if i==5:
---> 14             1/0
     15         time.sleep(1)
     16 

ZeroDivisionError: division by zero
'''

子プロセスがエラーを起こしても、親プロセスはそのまま動き続ける。

通常の関数呼び出しでは、呼び出した関数にエラーが生じると、プログラムはそこで止まる。しかし、Poolを用いて生成した子プロセスにsleep_bug()を実行させると、子プロセスはエラーが発生して止まるが、親プロセスではエラーは発生せずに最後まで進んでしまう。

from multiprocessing import Pool
p = Pool(1)
r = p.apply_async(sleep_bug)
p.close()
p.join() #子プロセスが終了するまで待つ。
print('Done')

# output
'''
sleeping 0
sleeping 1
sleeping 2
sleeping 3
sleeping 4
sleeping 5
Done
'''

子プロセスにエラーが生じて止まった時に、親プロセスにもエラーが伝わるようにするには、r.get()を用いる。r.get()は通常時には子プロセスが終了するのを待ち、子プロセスの戻り値を出力する関数であるが、子プロセスでエラーが起こった場合には、r.get()は例外を送出し、親プロセスもそこで止まるようになる。

from multiprocessing import Pool
p = Pool(1)
r = p.apply_async(sleep_bug)
p.close()
output = r.get()
print('Done %d' % output)

# 出力
'''
sleeping 0
sleeping 1
sleeping 2
sleeping 3
sleeping 4
sleeping 5
---------------------------------------------------------------------------
RemoteTraceback                           Traceback (most recent call last)
RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 121, in worker
    result = (True, func(*args, **kwds))
  File "<ipython-input-41-26bb27998e63>", line 14, in sleep_bug
    1/0
ZeroDivisionError: division by zero
"""

The above exception was the direct cause of the following exception:

ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-50-fb8f5892e1a7> in <module>
      3     r = p.apply_async(sleep_bug)
      4     p.close()
----> 5     output = r.get()
      6     print('Done %d' % output)

/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py in get(self, timeout)
    655             return self._value
    656         else:
--> 657             raise self._value
    658 
    659     def _set(self, i, obj):

ZeroDivisionError: division by zero
'''

Processを用いた場合には、エラーは表示されるが、親プロセスは最後まで進む。

from multiprocessing import Process
p = Process(target=sleep_bug)
p.start()
p.join()
print('Done')

#出力
'''
sleeping 0
sleeping 1
sleeping 2
sleeping 3
sleeping 4
sleeping 5
Process Process-35:
Traceback (most recent call last):
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/process.py", line 297, in _bootstrap
    self.run()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/process.py", line 99, in run
    self._target(*self._args, **self._kwargs)
  File "<ipython-input-41-26bb27998e63>", line 14, in sleep_bug
    1/0
ZeroDivisionError: division by zero
Done
'''

親プロセスが止まっても子プロセスは動き続ける

下のスクリプトは、子プロセスが実行中2秒待ち、親プロセスはsys.exit()でそこで自分自身を終了させるものである。以下の実行例のように、親プロセスは途中で止まっても、子プロセスは動き続ける。

from multiprocessing import Pool
p = Pool(1)
r = p.apply_async(sleep)
p.close()
r.wait(2) #子プロセスを2秒待つ
sys.exit()

#出力
'''
sleeping 0
sleeping 1
An exception has occurred, use %tb to see the full traceback.

SystemExit
sleeping 2
sleeping 3
sleeping 4
sleeping 5
sleeping 6
sleeping 7
sleeping 8
sleeping 9
'''
3
5
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
3
5