Это сильно зависит от A) ваших данных и B) вашего многопроцессорного метода .
TLDR:
spawn
объекты клонируются и каждый завершается в каждом процессе fork
/ forkserver
объекты совместно используются и финализируются в основном процессе
Некоторые объекты плохо реагируют на финализацию в основном процессе, хотя все еще используются в дочерних процессах.
- Документы на
args
неверны , поскольку содержимое args
само по себе не поддерживается (3.7.0)
Примечание: Полный код доступен в виде gist .Весь вывод из CPython 3.7.0 в macOS 10.13.
Мы начнем с простого объекта, который сообщает, где и когда он был завершен:
def print_pid(*args, **kwargs): # Process aware print helper
print('[%s]' % os.getpid(), *args, **kwargs)
class Finalisable:
def __init__(self, name):
self.name = name
def __repr__(self):
return '<Finalisable object %s at 0x%x>' % (getattr(self, 'name', 'unknown'), id(self))
def __del__(self):
print_pid('finalising', self)
Ранний сбор с args
Чтобы проверить, как args
работает для GC, мы можем построить процесс и немедленно освободить ссылку на его аргумент:
def drop_early():
payload = Finalisable()
child = multiprocessing.Process(target=print, args=(payload,))
print('drop')
del payload # remove sole local reference for `args` content
print('start')
child.start()
child.join()
С помощью метода spawn
оригинал собирается, но у дочернего элемента есть свой собственный.копия для завершения:
### test drop_early in 15333 method: spawn
drop
start
[15333] finalising <Finalisable object early at 0x102347390>
[15336] child sees <Finalisable object early at 0x109bd8128>
[15336] finalising <Finalisable object early at 0x109bd8128>
### done
С помощью метода fork
оригинал завершается , и ребенок получает этот завершенный объект :
### test drop_early in 15329 method: fork
drop
start
[15329] finalising <Finalisable object early at 0x108b453c8>
[15331] child sees <Finalisable object early at 0x108b453c8>
### done
Это показывает, чтополезная нагрузка основного процесса завершена за до , дочерний процесс запускается и завершается! Итог, args
не является защитой от раннего сбора!
Ранний сбор общих объектов
В Python есть некоторые типы, предназначенные для безопасного обмена между процессами.Мы также можем использовать это как маркер:
def drop_early_shared():
payload = Finalisable(multiprocessing.Value('i', 65))
child = multiprocessing.Process(target=print_pid, args=('child sees', payload,))
print('drop')
del payload
print('start')
child.start()
child.join()
С помощью метода fork
, Value
собирается рано, но все еще функционально:
### test drop_early_shared in 15516 method: fork
drop
start
[15516] finalising <Finalisable object <Synchronized wrapper for c_int(65)> at 0x1071a3e10>
[15519] child sees <Finalisable object <Synchronized wrapper for c_int(65)> at 0x1071a3e10>
### done
С spawn
метод, Value
собирается рано и полностью разбит для ребенка:
### test drop_early_shared in 15520 method: spawn
drop
start
[15520] finalising <Finalisable object <Synchronized wrapper for c_int(65)> at 0x103a16c18>
[15524] finalising <Finalisable object unknown at 0x101aa0128>
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/spawn.py", line 105, in spawn_main
exitcode = _main(fd)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/spawn.py", line 115, in _main
self = reduction.pickle.load(from_parent)
File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/synchronize.py", line 111, in __setstate__
self._semlock = _multiprocessing.SemLock._rebuild(*state)
FileNotFoundError: [Errno 2] No such file or directory
### done
Это показывает, что поведение при финализации зависит от вашего объекта и вашей среды. Итог, не думайте, что ваш объект хорошо себя ведет!
Хотя рекомендуется передавать данные через args
, это не освободить основной процесс от его обработки!Объекты могут плохо реагировать на раннее завершение, когда основной процесс отбрасывает ссылки.
Поскольку CPython использует быстродействующий счетчик ссылок, вы заметите вредные эффекты практически сразу.Однако другие реализации, например PyPy, могут скрывать такие побочные эффекты в течение произвольного времени.