如果只有一个线程,异步任务如何说它们是 "done"?

How do asynchronous tasks say they're "done" if there's one thread?

我已经使用 Tornado 和 asyncio 编写了异步程序,但是我意识到我不明白异步任务是如何表示它们已完成的。

例如,让我们看一下http://www.tornadoweb.org/en/stable/guide/async.html#examples中的异步获取。

目前我的理解是:

  1. 处理程序在 fetch 产生 Future 时挂起。
  2. Future 通过 http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.add_future
  3. 添加到 IOLoop
  4. Future 完成,IOLoop 安排协程重新激活以便完成。

我不明白的是 Future 在步骤 3 "finishes" 中如何调用它的 done callback。我以为只有一个线程,那么 Future "work in the background" 如何获得控制权以便调用回调?

IOLoop 打开一个套接字到您要从中获取的远程服务器,并使用 epoll 或类似的系统调用将该套接字添加到它正在等待 IO 的文件描述符列表中。

每当循环不执行您的代码时——例如,当您的处理程序被 yield 暂停时,循环正在等待 IO,此处:

https://github.com/tornadoweb/tornado/blob/master/tornado/ioloop.py#L862

当它接收到一个 IO 事件时——例如,当远程服务器发送一些响应字节时——Tornado 找到等待该事件的回调并执行它。

有关事件循环的示例实现,请参阅 A Web Crawler With asyncio Coroutines