4

我正在uvloop使用websocketsas

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方法,而是直接与传递的事件循环一起工作。那是对的吗?

4

2 回答 2

6

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

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

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

要记住的一件事是,从 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()在协程之外调用会导致一个新的标准异步循环,设置为这个线程的默认循环。

因此,前两种方法很好,但不鼓励使用第三种方法。

于 2017-05-16T12:05:57.573 回答
3

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

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

await asyncio.sleep(1, loop=loop)

或者

fut = asyncio.Future(loop=loop)

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

正如您从它的源代码中看到的那样,同样的事情也适用于websockets库。所以你需要写:

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

如果您不将事件循环作为参数传递,则无法保证您的程序可以正常工作。

可能,但不舒服

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

  • loop=loop几乎必须在任何地方写,这很烦人

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

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

我知道使用全局事件循环可能会让人觉得“不正确”,但“正确”的方法是在任何地方传递循环作为参数在实践中更糟(在我看来)。

于 2017-05-16T12:05:44.937 回答