Python: multiprocessing モジュールの Pool.map を使ったときの罠
Pool.map を使った並行処理をバッチ処理などで実行した際、キーボードによる中断(KeyboardInterrupt)をすると
プロセスがハングアップすることがある。
コード
#!/usr/bin/env python from multiprocessing import Pool, current_process import time from datetime import datetime def f(x): print('[%s] start %d: %s' % (datetime.now(), x, current_process().name)) time.sleep(5) print('[%s] end %d: %s' % (datetime.now(), x, current_process().name)) return x pool = Pool(2) ret = pool.map(f, range(4)) print('[%s] result: %s' % (datetime.now(), ret))
通常の実行例
[2015-12-31 22:10:02.319094] start 0: PoolWorker-1 [2015-12-31 22:10:02.319267] start 1: PoolWorker-2 [2015-12-31 22:10:07.320538] end 1: PoolWorker-2 [2015-12-31 22:10:07.320538] end 0: PoolWorker-1 [2015-12-31 22:10:07.321345] start 2: PoolWorker-2 [2015-12-31 22:10:07.321434] start 3: PoolWorker-1 [2015-12-31 22:10:12.321765] end 2: PoolWorker-2 [2015-12-31 22:10:12.321765] end 3: PoolWorker-1 [2015-12-31 22:10:12.322721] result: [0, 1, 2, 3]
Ctrl-C を押した場合
[2015-12-31 22:05:42.108238] start 0: PoolWorker-1 [2015-12-31 22:05:42.109395] start 1: PoolWorker-2 ^CProcess PoolWorker-1: Process PoolWorker-2: Traceback (most recent call last): Traceback (most recent call last): (snip) return map(*args) time.sleep(5) File "./x.py", line 9, in f KeyboardInterrupt time.sleep(5) KeyboardInterrupt [2015-12-31 22:05:42.631444] start 2: PoolWorker-3 [2015-12-31 22:05:42.632162] start 3: PoolWorker-4 [2015-12-31 22:05:47.632713] end 3: PoolWorker-4 [2015-12-31 22:05:47.632664] end 2: PoolWorker-3
プログラムが停止せず、kill コマンドでプロセスを終了する必要に迫られる。
何度 Ctrl-C を押しても駄目である。
回避策1
try 節で KeyboardInterrupt をトラップすれば、プログラムのハングアップは防げる。
しかし、中断した後もプログラムが続行してしまう。
#!/usr/bin/env python from multiprocessing import Pool, current_process import time from datetime import datetime def f(x): try: print('[%s] start %d: %s' % (datetime.now(), x, current_process().name)) time.sleep(5) print('[%s] end %d: %s' % (datetime.now(), x, current_process().name)) except KeyboardInterrupt: pass return x pool = Pool(2) ret = pool.map(f, range(4)) print('[%s] result: %s' % (datetime.now(), ret))
実行例
[2015-12-31 22:21:41.647775] start 0: PoolWorker-1 [2015-12-31 22:21:41.647928] start 1: PoolWorker-2 ^C[2015-12-31 22:21:42.716623] start 2: PoolWorker-2 [2015-12-31 22:21:42.716755] start 3: PoolWorker-1 [2015-12-31 22:21:47.717815] end 2: PoolWorker-2 [2015-12-31 22:21:47.717832] end 3: PoolWorker-1 Traceback (most recent call last): (snip) waiter.acquire() KeyboardInterrupt
回避策2
map の代わりに map_async を使えば、問題を回避できる。この場合は即座にプログラムが終了する。
ただし、結果を利用する際には get メソッドとともにタイムアウトの時間(秒数)を指定する必要がある。
#!/usr/bin/env python from multiprocessing import Pool, current_process import time from datetime import datetime def f(x): print('[%s] start %d: %s' % (datetime.now(), x, current_process().name)) time.sleep(5) print('[%s] end %d: %s' % (datetime.now(), x, current_process().name)) return x pool = Pool(2) p = pool.map_async(f, range(4)) ret = p.get(86400) print('[%s] result: %s' % (datetime.now(), ret))
実行例
[2015-12-31 22:24:32.482984] start 0: PoolWorker-1 [2015-12-31 22:24:32.483816] start 1: PoolWorker-2 ^CTraceback (most recent call last): (snip) time.sleep(5) KeyboardInterrupt
0 件のコメント:
コメントを投稿