当脚本在多处理工作线程中运行异步事件循环时,通过子进程运行脚本会挂起



在Windows上运行Python 3.7.3,

我遇到过asyncio事件循环永远不会从多处理生成的进程中中断的情况。我无法显示所有代码,但它是这样的:

  1. 我使用multiprocessing来加速使用第三方的查询 应用程序接口。
  2. 此 APIthirdparty.api支持服务器-客户端体系结构并使用异步 事件循环在内部。它在单独的线程中运行事件循环;在该线程中,它调用event_loop.run_forever()并仅在KeyboardInterrupt上中断。
  3. 使用多处理运行工作线程脚本,无论成功还是失败,API 始终返回。以前我遇到了 Py3.7.2 回归,在 Windows 上,venv Python 可执行文件以糟糕的方式工作 https://bugs.python.org/issue35797。但是现在这在 Py3.7.3 中已修复,我的问题仍然存在。
  4. 使用subprocess从另一个 Py27 脚本运行此脚本。在我的多处理工作进程中,如果查询失败,调用永远不会返回,并且它无法自然地脱离工作进程,即使是通用异常处理程序也不会捕获任何内容并且会卡住。

我的调用方脚本的代码片段:

#!/usr/bin/env python2
def main()
try:
cmd = ['C:\Python\Python37\pythonw.exe', 'worker.py']
print(' '.join(cmd))
proc = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
out, err = proc.communicate()
except subprocess.CalledProcessError as err:
print(err)
except Exception:
traceback.print_exc()
else:
print('try popen finished with else.')
print('stdout: {}'.format(out))
print('stderr: {}'.format(err))

if __name__ == '__main__':
main()

我的工作线程worker.py函数的伪代码片段如下所示:

#!/usr/bin/env python3
args = [
...
]

def worker(*mpargs):
with thirdparty.api() as myclient:
try:
myclient.query(*args)
except Exception:
traceback.print_exc()

def exception_worker(*mpargs)
raise RuntimeError('Making trouble!')

def main():
logging.info('STARTED!')
with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
results = pool.map(worker, args)
# results = pool.map(exception_worker, args)
pool.close()
pool.join()
logging.info('ALL DONE!')

if __name__ == '__main__':
main()

第三方.API 在其构造函数中启动事件循环:

self.loop = asyncio.get_event_loop()
if self.loop.is_closed():
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)

然后在其单独的线程中:

try:
self._loop.run_forever()
except KeyboardInterrupt:
pass
self.loop.close()

我尝试了另一个只抛出异常的工人exception_worker,这个返回没有问题。

我应该如何解决这个问题?

在详细介绍了问题之后,我终于在这篇文章中找到了解决方案: 为什么我在 Windows 上收到异步和等待的 NotImplementError ?

thirdparty.api需要注意这个细节,修复后,我的问题就消失了。

最新更新