首页 > 解决方案 > 在异步循环中调用 os.fork() 时的设计行为是什么?

问题描述

asyncio 可以与 os.fork() 一起使用吗?

代码片段 1:

import asyncio
import os

import aiohttp


async def main():
    url = "https://google.com"
    pid = os.fork()
    if pid == 0:
        # child process
        print("in child process")
        await fetch(url)
        print("in child process done")
    else:
        print("in parent process")
        await asyncio.sleep(20)
        print("in parent process done")


async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

if __name__ == "__main__":
    asyncio.run(main())

上面的代码工作正常。

代码片段 2:

import asyncio
import os

import aiohttp


async def main():
    url = "https://google.com"
    pid = os.fork()
    if pid == 0:
        # child process
        print("in child process")
        await asyncio.sleep(10)                  # different with code snippet 1
        # await fetch(url)
        print("in child process done")
    else:
        print("in parent process")
        await asyncio.sleep(20)
        print("in parent process done")


async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

if __name__ == "__main__":
    asyncio.run(main())

上面的代码将引发以下异常:

Traceback (most recent call last):
  File "fork_sleep.py", line 28, in <module>
    asyncio.run(main())
  File "/usr/lib/python3.8/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "fork_sleep.py", line 13, in main
    await asyncio.sleep(10)                  # different with code snippet 1
  File "/usr/lib/python3.8/asyncio/tasks.py", line 637, in sleep
    loop = events.get_running_loop()
RuntimeError: no running event loop

“没有运行事件循环”异常的原因是函数 get_running_loop 比较了 os.getpid() 和保存在循环中的 pid。当它们不同时,会引发上述异常。

请参考cpython源代码中的以下代码。

def get_running_loop():
    """Return the running event loop.  Raise a RuntimeError if there is none.

    This function is thread-specific.
    """
    # NOTE: this function is implemented in C (see _asynciomodule.c)
    loop = _get_running_loop()
    if loop is None:
        raise RuntimeError('no running event loop')
    return loop


def _get_running_loop():
    """Return the running event loop or None.

    This is a low-level function intended to be used by event loops.
    This function is thread-specific.
    """
    # NOTE: this function is implemented in C (see _asynciomodule.c)
    running_loop, pid = _running_loop.loop_pid
    if running_loop is not None and pid == os.getpid():
        return running_loop

因此,如果您不触摸函数 get_running_loop,似乎 asyncio 事件循环在子进程中可以正常工作。我的问题是,设计行为是什么?为什么作者检查函数_get_running_loop中的pid?如果您在子进程中遇到“没有运行的事件循环”,解决方案是什么。

标签: pythonforkpython-asyncioevent-loop

解决方案


推荐阅读