使用备用事件循环而不设置全局策略

时间:2017-05-16 11:23:10

标签: python-asyncio uvloop

我正在使用uvloop websockets作为

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方法,而是直接使用传递的事件循环。这是对的吗?

2 个答案:

答案 0 :(得分:3)

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

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

await asyncio.sleep(1, loop=loop)

fut = asyncio.Future(loop=loop)

您可能会注意到asyncio模块中的任何协程/对象都可能接受此参数。

同样的事情也适用于websocketsmay see来自它的源代码。所以你需要写:

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

如果您未能将事件循环作为这样的参数传递,那么您的程序无法保证正常工作。

可能,但不舒服

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

  • 你必须在几乎所有地方写loop=loop,这很烦人

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

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

我知道可能感觉到"不正常"使用全局事件循环,但是"对"方式是传递循环,因为param在实践中的每个地方都更糟糕(在我看来)。

答案 1 :(得分:3)

asyncio中有三个主要的全局对象:

  • 该政策(所有主题通用)
  • 默认循环(特定于当前线程)
  • 正在运行的循环(特定于当前线程)

在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()将返回正确的循环。

示例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()将返回正确的循环。

示例3:

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

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

在此示例中,在协程中调用get_event_loop()会返回正确的循环(正在运行的uvloop)。但是在协程外调用get_event_loop()将导致新的标准asyncio循环,设置为该线程的默认循环。

所以前两种方法都没问题,但第三种方法是不鼓励的。