在不设置全局策略的情况下使用替代事件循环

Using alternative event loop without setting global policy

我正在使用 uvloopwebsockets 作为

import uvloop
coro = websockets.serve(handler, host, port)  # creates new server
loop = uvloop.new_event_loop()
loop.create_task(coro)
loop.run_forever()

它工作正常,我只是想知道如果不将全局 asyncio 策略设置为 uvloop,我是否可以 运行 一些意想不到的问题。据我所知,只要下面没有任何内容不使用全局 asyncio 方法,而是直接与传递的事件循环一起使用,那么不设置全局策略应该是有效的。对吗?

自定义事件循环应作为参数传递

如果您想在不使用 asyncio.set_event_loop(loop) 的情况下使用自定义事件循环,则必须将循环作为参数传递给每个相关的异步协程或对象,例如:

await asyncio.sleep(1, loop=loop)

fut = asyncio.Future(loop=loop)

您可能会注意到 asyncio 模块中的任何 coroutine/object 可能都接受此参数。

同样的事情也适用于 websockets 库,因为你 may see 来自它的源代码。所以你需要写:

loop = uvloop.new_event_loop()
coro = websockets.serve(handler, host, port, loop=loop)  # pass loop as param

如果您不像这样将事件循环作为参数传递,则不能保证您的程序会正常运行。

可能,但不舒服

虽然理论上您可以在不更改策略的情况下使用某些事件循环,但我发现这非常不舒服。

  • 几乎到处都要写loop=loop,很烦人

  • 不能保证某些第三方会允许您通过 作为参数循环并且不会只使用 asyncio.get_event_loop()

基于此,我建议您重新考虑您的决定并使用全局事件循环。

我知道 "unright" 使用全局事件循环可能会让人觉得,但是 "right" 方法是传递循环,因为在实践中到处都是参数更糟糕(在我看来)。

asyncio中主要有3个全局对象:

  • 策略(所有线程通用)
  • 默认循环(特定于当前线程)
  • 运行 循环(特定于当前线程)

在 asyncio 中获取当前上下文的所有尝试都通过一个函数 asyncio.get_event_loop

要记住的一件事是 since Python 3.6(和 Python 3.5.3+),get_event_loop 有一个特定的行为:

  • 如果在循环 运行 时调用它(例如在协程中),则返回 运行 循环。
  • 否则,策略将返回默认循环。

示例 1:

import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
loop = asyncio.get_event_loop()
loop.run_forever()

这里的策略是uvloop策略。 get_event_loop 返回的循环是一个 uvloop,它被设置为该线程的默认循环。当此循环为 运行 时,它被注册为 运行 循环。

在此示例中,调用 get_event_loop() 此线程中的任何位置 returns 正确的循环。

示例 2:

import uvloop
loop = uvloop.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_forever()

这里的策略还是默认的策略。 new_event_loop 返回的循环是一个 uvloop,使用 asyncio.set_event_loop 显式将其设置为该线程的默认循环。当此循环为 运行 时,它被注册为 运行 循环。

在此示例中,调用 get_event_loop() 此线程中的任何位置 returns 正确的循环。

示例 3:

import uvloop
loop = uvloop.new_event_loop()
loop.run_forever()

这里的策略还是默认的策略。 new_event_loop 返回的循环是一个 uvloop,但它没有设置为该线程的默认循环。当此循环为 运行 时,它被注册为 运行 循环。

在此示例中,在协程 returns 右循环(运行 uvloop)中调用 get_event_loop()。但是在协程外调用 get_event_loop() 将导致一个新的标准异步循环,设置为该线程的默认循环。

所以前两种方法都可以,但不鼓励第三种方法。