了解 python 中的异步

Understanding asyncio in python

我一直很难理解 Python 的 asyncio 模块以及如何不阻塞异步调用。例如,给定此代码片段:

import aiohttp
import asyncio
import async_timeout

async def fetch(session, url):
    with async_timeout.timeout(10):
        async with session.get(url) as response:
            return await response.text()

async def main(loop):
    print(1)
    async with aiohttp.ClientSession(loop=loop) as session:
        html = await fetch(session, 'http://python.org')
        print(html)
    print(2)

loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))

我预计,与 Javascript 中的工作方式类似,输出为

1
2
<!doctype html>
<...>

相反,该函数打印 1,阻塞直到它返回 html,然后打印 2。它为什么阻塞,我如何/如何避免阻塞?感谢您的帮助。

你的问题是这一行:

html = await fetch(session, 'http://python.org')

这意味着 - 等待直到从 fetch 获得响应,或者换句话说,它会阻塞 current 协程,直到 fetch 协程 return 的响应文本。

为了让事情更简单:

  • 提取操作本身不阻塞,只有等待。
  • await 只阻塞当前协程,而不是线程。
  • 就像vincent说的那样,调用操作本身并没有做任何事情(我们不启动抓取过程)

来自 asyncio documentation

Calling a coroutine does not start its code running – the coroutine object returned by the call doesn’t do anything until you schedule its execution. There are two basic ways to start it running: call await coroutine or yield from coroutine from another coroutine (assuming the other coroutine is already running!), or schedule its execution using the ensure_future() function or the AbstractEventLoop.create_task() method.

和修复(以获得所需的输出):

import asyncio
import aiohttp
import async_timeout


async def fetch(session, url):
    with async_timeout.timeout(10):
        async with session.get(url) as response:
            return await response.text()


async def main(loop):
    print(1)
    async with aiohttp.ClientSession(loop=loop) as session:
        future = asyncio.ensure_future(fetch(session, 'http://python.org')) # we start the fetch process
        print(2)
        html = await future # we wait for getting the fetch response
        print(html)


loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))

为了理解 python 3.6 async def 风格的 asyncio,我建议你 运行 这个简单的例子:

import asyncio
import time

def write(msg):
    print(msg, flush=True)

async def say1():
    await asyncio.sleep(1)
    write("Hello 1!")

async def say2():
    await asyncio.sleep(1)
    write("Hello 2!")

write("start")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
    say1(),
    say2()
))
write("exit")

loop.close()

来源:https://maketips.net/tip/146/parallel-execution-of-asyncio-functions有关带有调用图的代码的详细说明,请参见link。