【问题标题】:asyncio + multiprocessing + unix异步 + 多处理 + unix
【发布时间】:2016-11-06 17:02:27
【问题描述】:

我有一个具有以下逻辑的宠物项目:

import asyncio, multiprocessing

async def sub_main():
    print('Hello from subprocess')

def sub_loop():
    asyncio.get_event_loop().run_until_complete(sub_main())

def start():
    multiprocessing.Process(target=sub_loop).start()

start()

如果你运行它,你会看到:

Hello from subprocess

这很好。但我要做的是改为使start()协程:

async def start():
    multiprocessing.Process(target=sub_loop).start()

要运行它,我必须这样做:

asyncio.get_event_loop().run_until_complete(start())

问题是:创建子进程时,它会克隆整个 Python 环境,因此事件循环已经在那里运行:

Process Process-1:
Traceback (most recent call last):
  File "/usr/lib/python3.5/multiprocessing/process.py", line 249, in _bootstrap
    self.run()
  File "/usr/lib/python3.5/multiprocessing/process.py", line 93, in run
    self._target(*self._args, **self._kwargs)
  File "test.py", line 7, in sub_loop
    asyncio.get_event_loop().run_until_complete(sub_main())
  File "/usr/lib/python3.5/asyncio/base_events.py", line 361, in run_until_complete
    self.run_forever()
  File "/usr/lib/python3.5/asyncio/base_events.py", line 326, in run_forever
    raise RuntimeError('Event loop is running.')
RuntimeError: Event loop is running.

我试图在没有运气的情况下在子进程端销毁它,但我认为正确的方法是防止它与子进程共享。有可能吗?

更新: 这是完整的失败代码:

import asyncio, multiprocessing

import asyncio.unix_events

async def sub_main():
    print('Hello from subprocess')

def sub_loop():
    asyncio.get_event_loop().run_until_complete(sub_main())


async def start():
    multiprocessing.Process(target=sub_loop).start()

asyncio.get_event_loop().run_until_complete(start())

【问题讨论】:

  • 我没有时间给出完整的答案,但您可能需要考虑一种设计,其中 (a) 您的多处理工作由可以使用例如调用的脚本完成。 subprocess.Popen([sys.executable, "the_script.py"], ...) (b) 这个脚本与它的父级通信,例如。 stdout 使用设计好的协议(它可能非常简单,例如脚本的单字节控制字符和状态更新)和 (c) 使用 asyncio subprocess API
  • (我并不是说你应该同时使用subprocess.Popen 和 asyncio 的子进程 API,只是你应该编写你的脚本,以便它可以被控制为任何与语言无关的子进程。)
  • @detly 感谢您的建议,但是有很多数据应该由子进程继承。如果有一个简单的解决方案可以避免上述问题,我更喜欢它而不是手动重写所有多处理的东西。
  • 这很公平,这不是一件小事。
  • 我认为这是可能的,因为我发现了一个似乎有效但仅在 unix 平台上有效的 hack。 sub_loop 可以从 asyncio.set_event_loop(asyncio.unix_events._UnixSelectorEventLoop()) 开始,这将为子进程创建一个新循环,而父进程将(希望)被垃圾收集

标签: python unix python-3.5 python-multiprocessing python-asyncio


【解决方案1】:

您应该始终添加检查以查看您是如何运行代码的(if __name__ == '__main__': 部分。您的子进程第二次运行模块中的所有内容,给您带来悲伤(可能t抵抗)。

import asyncio, multiprocessing

async def sub_main():
    print('Hello from subprocess')

def sub_loop():
    asyncio.get_event_loop().run_until_complete(sub_main())


async def start():
    multiprocessing.Process(target=sub_loop).start()

if __name__ == '__main__':
    asyncio.get_event_loop().run_until_complete(start())

【讨论】:

  • 我猜这只是与 unix 相关的,因为这最终会得到相同的结果,我的意思是完全相同的异常。
  • @Grief:我看看能否在 linux 环境中复制您的问题。
  • 顺便说一句,他们在 python3 文档中更改了该部分。在 docs.python.org/2/library/… 中是 For an explanation of why (on Windows) the if __name__ == '__main__' part is necessary, see Programming guidelines.,现在只是 For an explanation of why the if __name__ == '__main__' part is necessary, see Programming guidelines. 我相信这里没有任何变化,删除 Windows 只是对跨平台编码的推动。
  • @Grief:...好吧,现在你的问题很有趣(对我来说)。在 windows 和 linux 上的结果不同。我让你的异常在 linux 上运行。
  • 我想我可以尝试捕获异常并在发生错误时执行asyncio.set_event_loop(asyncio.unix_events._UnixSelectorEventLoop())。至少目前是一种解决方法。不过,我更喜欢更可靠的东西。
【解决方案2】:

首先,如果您计划在循环中运行 python 子进程,您应该考虑使用 loop.run_in_executorProcessPoolExecutor。至于你的问题,你可以使用event loop policy函数来设置一个新的循环:

import asyncio
from concurrent.futures import ProcessPoolExecutor

async def sub_main():
    print('Hello from subprocess')

def sub_loop():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(sub_main())

async def start(executor):
    await asyncio.get_event_loop().run_in_executor(executor, sub_loop)

if __name__ == '__main__':
    executor = ProcessPoolExecutor()
    asyncio.get_event_loop().run_until_complete(start(executor))

【讨论】:

  • 对...我好像瞎了,因为我错过了这个明显的功能asyncio.new_event_loop()。谢谢!能否请您解释一下ProcessPoolExecutor 在这种情况下的利润是多少?
  • @Grief run_in_executor 是一个协程,因此您可以使用 awaitasyncio.wait_for 轻松加入您的子进程。 ProcessPoolExecutor 还允许您指定工作人员的数量。
  • 感谢您指出需要在已创建的子流程中创建新的事件循环。这是我缺少的关键位 - 否则会得到一个非常神秘的 Bad file descriptor 错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-29
  • 2018-12-11
  • 2011-03-18
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 2017-06-03
相关资源
最近更新 更多