关于python:多次实例化时asyncio Lock中的事件循环错误

Event loop error in asyncio Lock when instantiated multiple times

我在初始化Locks和运行异步代码时遇到了一些奇怪的错误。 假设我们有一个类可以与受锁保护的某些资源一起使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import asyncio

class C:
    def __init__(self):
        self.lock = asyncio.Lock()

    async def foo(self):
        async with self.lock:
            return 'foo'

def async_foo():
    c = C()
    asyncio.run(c.foo())

if __name__ == '__main__':
    async_foo()
    async_foo()

运行时会引发错误。 它在init中的锁初始化时发生。

RuntimeError: There is no current event loop in thread 'MainThread'.

因此,在函数中复制asyncio.run调用不会产生此效果。 似乎该对象需要多次初始化。 在单个构造函数中实例化多个锁也是不够的。 因此,也许它与调用asyncio.run之后的事件循环状态有关。

到底是怎么回事? 而我该如何修改此代码才能正常工作? 让我也澄清一下,实例是在asyncio.run和异步函数之外创建的,这是有原因的。 我希望它也可以在其他地方使用。 如果那有所作为。

或者,threading.Lock也可以用于异步事物吗? 它具有线程安全的附加好处,据报道asyncio.Lock并非如此。


What is going on?

  • 创建异步对象(asyncio.Lock())时,它将附加到当前事件循环,并且只能与它一起使用
  • 主线程具有一些默认的当前事件循环(但是您创建的其他线程将没有默认的事件循环)
  • asyncio.run()在内部创建新的事件循环,将其设置为当前并在完成后关闭

因此,您尝试将锁定与事件循环一起使用,而不是将其与创建时附加的事件循环一起使用。这会导致错误。

And how could I modify this code to work?

理想的解决方案如下:

1
2
3
4
5
6
7
8
9
import asyncio


async def main():
    # all your code is here


if __name__ =="__main__":
    asyncio.run(main())

这将确保创建的每个异步对象都附加到已创建的正确事件循环asyncio.run

运行事件循环(在asyncio.run内部)是异步程序的全局"入口点"。

I'd like for it to be usable elsewhere too.

您可以在asyncio.run之外创建对象,但随后应从其他地方的__init__创建异步对象,以便在调用asyncio.run()之前不会创建asyncio.Lock()

Alternatively, can threading.Lock be used for async things also?

不,它用于处理线程,而asyncio通常在单个线程内操作协程。

It would have the added benefit of being thread-safe, which asyncio.Lock reportedly is not.

asyncio中,通常不需要main以外的线程。仍然有一些理由要这样做,但是asyncio.Lock的线程安全性不应该成为问题。

考虑阅读以下链接。可能有助于更好地理解情况:

  • 为什么我们需要异步/线程
  • 什么时候应该编写异步代码而不是同步代码?