[英]Python Process Pool with custom Process not able to respawn child processes
我已经像这样覆盖了multiprocess.Process
(多处理库的分支):
# source.Process.py
class Process(multiprocess.Process):
def __init__(self, test_name: str, *args, **kwargs) -> None:
multiprocess.Process.__init__(self, *args, **kwargs)
self._parent_conn, self._child_conn = multiprocess.Pipe()
self._exception = None
self._test_name = test_name
def run(self) -> None:
try:
start = time.perf_counter()
logger = S_Logger(self._test_name).get_logger()
logger.info('EXECUTION OF %s HAS STARTED.', self._test_name)
multiprocess.Process.run(self)
self._child_conn.send(None)
except Exception as e:
tb = traceback.format_exc()
logger.error(f'EXCEPTION OCCURRED: {tb}')
self._child_conn.send((e, tb))
finally:
logger.info('EXECUTION OF %s HAS ENDED.', self._test_name)
end = time.perf_counter()
logger.info(f'FINISHED in {round(end-start, 2)} second(s)')
当我使用此 class 创建正常流程时,一切正常,包括创建日志。 现在我想创建一个这样的自定义进程的进程池,但是我遇到了在它们生命结束后重生这些进程的问题。 这是我使用附加maxtasksperchild=1
参数创建池的方法。
from source.process import Process
ctx = multiprocess.get_context()
def run_tests(self):
def worker(x):
print(x**2)
time.sleep(1)
with ctx.Pool(processes=4, maxtasksperchild=1) as pool:
nums = range(10)
ctx.Process = Process(test_name='test_name')
pool.map(worker, nums)
这给了我这样的 output:
0
1
4
9
Exception in thread Thread-1 (_handle_workers):
Traceback (most recent call last):
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.2032.0_x64__qbz5n2kfra8p0\lib\threading.py", line 1016, in _bootstrap_inner
self.run()
File "C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.2032.0_x64__qbz5n2kfra8p0\lib\threading.py", line 953, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\<user>\Documents\Projects\sprinter\.venv\lib\site-packages\multiprocess\pool.py", line 513, in _handle_workers
cls._maintain_pool(ctx, Process, processes, pool, inqueue,
File "C:\Users\<user>\Documents\Projects\sprinter\.venv\lib\site-packages\multiprocess\pool.py", line 337, in _maintain_pool
Pool._repopulate_pool_static(ctx, Process, processes, pool,
File "C:\Users\<user>\Documents\Projects\sprinter\.venv\lib\site-packages\multiprocess\pool.py", line 319, in _repopulate_pool_static
w = Process(ctx, target=worker,
File "C:\Users\<user>\Documents\Projects\sprinter\.venv\lib\site-packages\multiprocess\pool.py", line 181, in Process
return ctx.Process(*args, **kwds)
TypeError: 'Process' object is not callable
这让我想到了两个问题:
maxtasksperchild
参数,它会完美运行(0、1、4、9、16、25 ...)这给了我这样的 output
这里的错误是因为您将 ctx.Process (一个类)替换为您自己的子类的实例。 实例,除非它们定义了__call__
方法,否则是不可调用的。 但即使你用你的子类替换它,它也行不通。 这是因为您将收到递归或属性错误,因为您将 class 替换为同一 class 的子类。
为什么没有日志记录? 如果我不使用池,日志会正确显示。
这是因为您从未真正成功地修补池 class 以使用您的 Process 子类,这也与您的第二个问题有关(请继续阅读)。
为什么在执行了四个进程之后,应该重新生成的新进程有问题要创建? (不可调用错误)。 如果我删除 maxtasksperchild 参数,它会完美运行(0、1、4、9、16、25 ...)
发生这种情况的原因是因为 pool 在您启动上下文管理器本身时创建了进程(在线ctx.Pool(processes=4, maxtasksperchild=1) as pool
)。 由于您是在进程启动后应用补丁,因此除非池再次启动进程(这是maxtasksperchild
的用武之地),否则它不会产生太大影响。 因此,如果您提供maxtasksperchild
,则池将尝试启动另一个进程,但由于补丁错误,它将返回错误。 如果您没有设置maxtasksperchild
,那么池将不会关心您应用的补丁,因为它不必再次启动进程。
无论如何,这里有一个更好的补丁来做你想做的事
from multiprocess.pool import Pool
from functools import partial
import multiprocess
import time
class Process(multiprocess.Process):
def __init__(self, *args, test_name='', **kwargs) -> None:
multiprocess.Process.__init__(self, *args, **kwargs)
self._parent_conn, self._child_conn = multiprocess.Pipe()
self._exception = None
self._test_name = test_name
def run(self) -> None:
# Have your own implementation here
pass
def _Process(ctx, *args, **kwds):
return ctx.MyProcess(*args, **kwds)
def worker(x):
print(x ** 2)
time.sleep(1)
if __name__ == "__main__":
ctx = multiprocess.get_context()
# Some patching, we add our subclass as an attribute to the context
ctx.MyProcess = Process
# Fix test_name to be passed as a kwarg whenever the pool starts a process. Pretty lazy but gets the job done.
test_name = 'test_name'
Pool.Process = partial(_Process, test_name=test_name)
with ctx.Pool(processes=4, maxtasksperchild=1) as pool:
nums = range(10)
pool.map(worker, nums)
注意test_name
现在是一个关键字参数并且也是可选的。 这是为了使它与functools.partial
一起工作。 您可能希望执行检查以使值通过并且有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.