如何在Jupyter笔记本中运行Python asyncio代码?

77

我有一些asyncio的代码,在Python解释器(CPython 3.6.2)中可以正常运行。现在,我想在带有IPython内核的Jupyter Notebook中运行它。

我可以使用下面的命令来运行:

import asyncio
asyncio.get_event_loop().run_forever()

虽然这似乎有效,但它也会阻塞笔记本电脑,并且似乎与笔记本电脑不兼容。

我的理解是Jupyter在幕后使用Tornado,因此我尝试按照Tornado文档中建议的方式安装Tornado事件循环

from tornado.platform.asyncio import AsyncIOMainLoop
AsyncIOMainLoop().install()

然而,这会导致以下错误:

---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-1-1139449343fc> in <module>()
      1 from tornado.platform.asyncio import AsyncIOMainLoop
----> 2 AsyncIOMainLoop().install()

~\AppData\Local\Continuum\Anaconda3\envs\numismatic\lib\site- packages\tornado\ioloop.py in install(self)
    179         `IOLoop` (e.g.,     :class:`tornado.httpclient.AsyncHTTPClient`).
    180         """
--> 181         assert not IOLoop.initialized()
    182         IOLoop._instance = self
    183 

AssertionError: 

最后我找到了以下页面:http://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Asynchronous.html

因此,我添加了一个单元格,其中包含以下代码:

import asyncio
from ipykernel.eventloops import register_integration

@register_integration('asyncio')
def loop_asyncio(kernel):
    '''Start a kernel with asyncio event loop support.'''
    loop = asyncio.get_event_loop()

    def kernel_handler():
        loop.call_soon(kernel.do_one_iteration)
        loop.call_later(kernel._poll_interval, kernel_handler)

    loop.call_soon(kernel_handler)
    try:
        if not loop.is_running():
            loop.run_forever()
    finally:
        loop.run_until_complete(loop.shutdown_asyncgens())
        loop.close()

接下来的单元格中,我运行了:

%gui asyncio

那样做虽然有效,但我并不真正理解为什么以及它是如何起作用的。有人可以请解释一下吗?

5个回答

76

2019年2月21日修正:问题已解决

在最新版本的Jupyter Notebook上,这不再是一个问题。Jupyter Notebook的作者在这里详细说明了此案例。

以下答案是原始回复,由操作员标记为正确。


虽然这是一段时间之前发布的内容,但如果其他人正在寻找关于在Jupyter Notebook中运行异步代码的解释和解决方法,则可以参考以下内容;

Jupyter的Tornado 5.0更新在添加其自己的asyncio事件循环后使asyncio功能无法使用:

Running on shell. Running on JupyterNotebook.

因此,在Jupyter Notebook上运行任何异步功能时,不能调用loop.run_until_complete(...),因为您将从asyncio.get_event_loop()接收的循环是活动的。

相反,您必须将任务添加到当前事件循环中:

import asyncio
loop = asyncio.get_event_loop()
loop.create_task(some_async_function())

或使用run_coroutine_threadsafe函数获取结果:

import asyncio
loop = asyncio.get_event_loop()
asyncio.run_coroutine_threadsafe(some_async_function(), loop)

这个行为有没有相关的文档? - Gary Li
非常有帮助,谢谢。+ 上面的代码是否需要关闭任何东西,还是已经完整了? - QHarr
3
如果你关闭循环,Jupyter 就会停止工作。 - felipe
1
再次感谢您提供如此出色的答案。 - QHarr
请注意,此答案已不再更新。这里是答案链接。 - Giorgio Balestrieri
1
@GiorgioBalestrieri 更新了帖子。 - felipe

73

1
感谢提供应用命令,我之前一直在使用旧的解决方法,有些卡住了 :) - Joël
从所有其他的解决方案中,这是最优雅的一个! - curiouscheese

22

在Jupyter中,我使用异步编程的灵光一现是这样的:

import time,asyncio

async def count():
    print("count one")
    await asyncio.sleep(1)
    print("count four")

async def count_further():
    print("count two")
    await asyncio.sleep(1)
    print("count five")

async def count_even_further():
    print("count three")
    await asyncio.sleep(1)
    print("count six")

async def main():
    await asyncio.gather(count(), count_further(), count_even_further())

s = time.perf_counter()
await main()
elapsed = time.perf_counter() - s
print(f"Script executed in {elapsed:0.2f} seconds.")

输出:

count one
count two
count three
count four
count five
count six
Script executed in 1.00 seconds.

这个例子最初让我感到困惑,但是它的原始来源在这里: https://realpython.com/async-io-python/


10

只需使用 %autoawait 魔法命令即可,

例如:

%autoawait asyncio

await some_function()

1
太好了!这对我有用,如果对其他人也有效,那么这应该是正确的答案。 - Jeroen Vermunt
这对我做了一些事情。 - DUMBA

3
我最近遇到一个问题,无法在Jupyter笔记本中运行asyncio代码。该问题在这里讨论: https://github.com/jupyter/notebook/issues/3397 我尝试了讨论中的其中一种解决方案,目前已经解决了这个问题。 pip3 install tornado==4.5.3 这替换了默认安装的tornado 5.x版本。
在Jupyter笔记本中的asyncio代码随后按预期运行。

你能否重新运行带有异步代码的Jupyter单元格? - igor
1
是的。到目前为止,一切都按预期工作。我不得不注释掉一些 loop.close() - dmmfll
那么,仅仅移除 loop.close 就能保证可重新运行性吗? - igor
我无法保证其可重复运行性,但在我工作的特定环境中,它按照我的预期运行。将Tornado降级到4.5.3版本后,RuntimeErrors问题得到解决。 - dmmfll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接