Python: multiprocessing.map: если один процесс вызывает исключение, почему не называются блоки finally, называемые другими процессами?




multithreading try-catch-finally (2)

finally повторно вызывает исходное исключение, если вы не return от него . Затем исключение вызывается Pool.map и уничтожает все ваше приложение. Подпроцессы прекращаются, и вы не видите никаких других исключений.

Вы можете добавить return к проглатыванию исключения:

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

Тогда вы должны иметь None в вашем map результате, когда произошло исключение.

Мое понимание состоит в том, что, наконец, предложения должны всегда выполняться, если попытка была введена.

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'])

Ожидаемый результат состоит в том, что для каждого из x, который напечатан сам по строке 8, должно быть возникновение «Наконец 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

Кажется, что исключение, завершающее один процесс, завершает процесс родительского и родственного брака, даже если в других процессах требуется дополнительная работа.

Почему я ошибаюсь? Почему это правильно? Если это правильно, как следует безопасно очищать ресурсы в многопроцессорном Python?


Короткий ответ: 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()

Каждый p в pool является 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

Так что дело доходит до того, что происходит, когда процесс Python в пакете try отправляется SIGTERM .

Рассмотрим следующий пример (test.py):

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

Если вы запустите его, отправьте ему SIGTERM , затем процесс немедленно завершится, без ввода finally набора, о чем свидетельствует отсутствие вывода и отсутствие задержки.

В одном терминале:

% test.py

Во втором терминале:

% pkill -TERM -f "test.py"

Результат в первом терминале:

Terminated

Сравните это с тем, что происходит, когда процесс отправляется SIGINT ( Cc ):

Во втором терминале:

% 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 .





try-catch-finally