can only join a child process

上次 gevent.hub.BlockingSwitchOutError: Impossible to call blocking function in the event loop callback 这篇文章记录的优雅退出的问题,前后还有些东西想记录一下。

一开始遇到的问题是本地运行某个服务(声明一下不是我写的),ctrl+c 结束时报错:

  File "/usr/local/lib/python3.6/multiprocessing/process.py", line 122, in join
    assert self._parent_pid == os.getpid(), 'can only join a child process'

经过调查(看代码查资料),发现原来是因为在启动多进程之前注册了信号处理的回调函数,并且在回调函数最后执行了对多进程的 join。有点像这样:

def run(self):
        ...

def start(self):
    # 注册 SIGINT 信号的回调函数
    signal.signal(signal.SIGINT, self.graceful_exit)

    # 启动一个心跳协程
    self.heartbeat_task = gevent.spawn(self.heartbeat)

    # 创建子进程
    self.workers = [multiprocessing.Process(
        target=self.run) for _ in range(4)]
    for worker in self.workers:
        worker.start()

def graceful_exit(self, sig, frame):
    ...
    # join 是等待进程结束
    for worker in self.workers:
        worker.join()

另外一点是,还在创建子进程前创建了一个 gevent 协程。而被用来的开发者忽略掉的重要问题就是:

创建子进程,也就是调用了操作系统的 fork,而 fork 会继承当前进程的信号处理回调函数以及协程,其实就是拷贝了整个当前进程,准确点说是写时拷贝,如果只是读的话,那读的都和原来进程是同一个内存。

明白了这一点,问题就很清晰了。

posted @ 2021-11-30 12:19  水郁  阅读(319)  评论(1编辑  收藏  举报
……