29

私の理解では、try が入力された場合、 finally 句は*常に* 実行する必要があります。

import random

from multiprocessing import Pool
from time import sleep

def Process(x):
  try:
    print x
    sleep(random.random())
    raise Exception('Exception: ' + x)
  finally:
    print 'Finally: ' + x

Pool(3).map(Process, ['1','2','3'])

予想される出力は、8 行目までに単独で出力される x ごとに、'Finally x' が出現することです

出力例:

$ python bug.py 
1
2
3
Finally: 2
Traceback (most recent call last):
  File "bug.py", line 14, in <module>
    Pool(3).map(Process, ['1','2','3'])
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/pool.py", line 225, in map
    return self.map_async(func, iterable, chunksize).get()
  File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/pool.py", line 522, in get
    raise self._value
Exception: Exception: 2

他のプロセスで実行する必要がある追加の作業があるにもかかわらず、1 つのプロセスを終了する例外が親プロセスと兄弟プロセスを終了するようです。

なぜ私は間違っているのですか?なぜこれが正しいのですか?これが正しい場合、マルチプロセス Python でリソースを安全にクリーンアップするにはどうすればよいですか?

4

3 に答える 3

34

短い答え:SIGTERM切り札finally

長い答え: でログを有効にしmp.log_to_stderr()ます:

import random
import multiprocessing as mp
import time
import logging

logger=mp.log_to_stderr(logging.DEBUG)

def Process(x):
    try:
        logger.info(x)
        time.sleep(random.random())
        raise Exception('Exception: ' + x)
    finally:
        logger.info('Finally: ' + x)

result=mp.Pool(3).map(Process, ['1','2','3'])

ログ出力には次が含まれます。

[DEBUG/MainProcess] terminating workers

これは、次のコードに対応しますmultiprocessing.pool._terminate_pool

    if pool and hasattr(pool[0], 'terminate'):
        debug('terminating workers')
        for p in pool:
            p.terminate()

pinpoolは でありmultiprocessing.Process、呼び出しterminate(少なくとも Windows 以外のマシンでは) は SIGTERM を呼び出します。

からmultiprocessing/forking.py:

class Popen(object)
    def terminate(self):
        ...
            try:
                os.kill(self.pid, signal.SIGTERM)
            except OSError, e:
                if self.wait(timeout=0.1) is None:
                    raise

tryつまり、スイート内の Python プロセスにSIGTERM.

次の例を考えてみましょう (test.py):

import time    
def worker():
    try:
        time.sleep(100)        
    finally:
        print('enter finally')
        time.sleep(2) 
        print('exit finally')    
worker()

それを実行してから送信するとSIGTERM、プロセスはスイートに入ることなくすぐに終了します。これfinallyは、出力がなく、遅延がないことからもわかります。

1 つの端末で:

% test.py

2 番目のターミナルで:

% pkill -TERM -f "test.py"

最初のターミナルでの結果:

Terminated

SIGINTプロセスに( C-c)が送信された場合と比較してください。

2 番目のターミナルで:

% pkill -INT -f "test.py"

最初のターミナルでの結果:

enter finally
exit finally
Traceback (most recent call last):
  File "/home/unutbu/pybin/test.py", line 14, in <module>
    worker()
  File "/home/unutbu/pybin/test.py", line 8, in worker
    time.sleep(100)        
KeyboardInterrupt

結論:SIGTERM切り札finally

于 2011-10-09T12:48:50.200 に答える
1

finally元の例外を発生させない限りreturn、元の例外を再発生させます。その後、例外が発生しPool.map、アプリケーション全体が強制終了されます。サブプロセスが終了し、他の例外は表示されません。

a を追加しreturnて、例外を飲み込むことができます。

def Process(x):
  try:
    print x
    sleep(random.random())
    raise Exception('Exception: ' + x)
  finally:
    print 'Finally: ' + x
    return

次に、例外が発生したときに結果を取得する必要がNoneあります。map

于 2011-10-09T01:58:19.983 に答える