【问题标题】:asyncio not stopping where expectedasyncio 没有在预期的地方停止
【发布时间】:2019-03-15 18:54:03
【问题描述】:

使用这个 asyncio 代码,我的导师说所有协程(这里是 50 个“fetch_page”)首先停止在第一个 async with 并等待,然后它们都从那里恢复并停止在第二个 async with,然后最后他们都回来了。

import aiohttp
import asyncio

async def fetch_page(url):
    print(1)
    async with aiohttp.ClientSession() as session:
       print(2)
        async with session.get(url) as response:
            print(3)
            return response.status

loop = asyncio.get_event_loop()    
tasks = [fetch_page('http://google.com') for i in range(50)]
loop.run_until_complete(asyncio.gather(*tasks))

我正在调试这个,我必须说他错了。在调试时,我看到所有协程都按顺序转到第二个async with,然后它们都停止了。然后,一旦所有 50 个协程恢复,它们就会执行 session.get(url) 并返回。

但为什么不是所有的协程都停在第一个 async with 处?

打印输出:“1 2 1 2 1 2 ... 3 3 3 ...”,而不是“1 1 1 ... 2 2 2 ... 3 3 3 ...”

【问题讨论】:

    标签: python python-asyncio


    【解决方案1】:

    但为什么不是所有的协程都停在第一个 async with 处?

    就像任何其他await 一样,async with 不保证协程会在那里暂停,但它允许如果有理由暂停协程。在这种情况下,仅仅创建一个ClientSession 是一个可以在不暂停任何协程的情况下完成的操作,因此它们每个都只是进一步进行。

    输入第二个async with必须得到一个响应对象,这要求请求已经发送,并且HTTP头已经接收。在非阻塞套接字上执行,其中一些操作会发出数据不是立即可用的信号(因为它需要到达服务器,服务器必须制作响应,然后响应必须返回给我们) , asyncio 通过挂起协程来处理它。这就是为什么第二个async with 实际上可以保证挂起每个到达它的协程。

    【讨论】:

      【解决方案2】:

      这里缺少一点信息:

      asyncio 是一个基于事件循环的单线程库。

      在这种情况下发生的情况是 aiohttp.ClientSession() as session 不是 I/O 昂贵的操作,它不需要时间来执行,这就是为什么它甚至在迭代器进入下一个循环周期之前就立即执行。

      为什么3333 会最后打印?因为您正在制作一个需要时间的 I/O,而不是循环 50 次。

      更多详情请阅读:asyncio python doc

      【讨论】:

      • 太棒了!我说得对吗:每个协程都在做他们的工作,没有停止,但是 async/await 允许暂停/恢复执行,以便其他协程可以进入做他们的工作。这不是多线程,都是在主线程上。
      • @trogne 当所有线程需要超过由操作系统任务调度程序确定的特定时间段时,它们都可能会暂停/恢复。所以是的 async/await 将使您的代码的“异步”部分怀疑被挂起。
      • 协程不会“委托给其他线程”,asyncio 是单线程的。
      猜你喜欢
      • 2022-12-22
      • 1970-01-01
      • 1970-01-01
      • 2018-04-24
      • 1970-01-01
      • 2017-10-15
      • 1970-01-01
      • 1970-01-01
      • 2012-06-23
      相关资源
      最近更新 更多