Ошибка события l oop в asyncio Lock при многократном создании - PullRequest
1 голос
/ 02 февраля 2020

Я сталкиваюсь с некоторыми странными ошибками при инициализации Locks и выполнении асинхронного кода. Предположим, у нас есть класс для использования с некоторым ресурсом, защищенным блокировкой.

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: В потоке 'MainThread' нет текущего события l oop.

Так что дублирование asyncio.run вызов в функции не имеет этого эффекта. Кажется, что объект должен быть инициализирован несколько раз. Также недостаточно создать несколько блокировок в одном конструкторе. Так что, возможно, это как-то связано с состоянием циклов событий после вызова asyncio.run.

Что происходит? И как я могу изменить этот код для работы? Позвольте мне также уточнить, что экземпляр создается за пределами функций asyncio.run и asyn c по определенной причине. Я бы хотел, чтобы его можно было использовать и в других местах. Если это имеет значение.

В качестве альтернативы, можно ли threading.Lock использовать также для асин c вещей? Дополнительным преимуществом было бы то, что он безопасен для потоков, что, как сообщается, asyncio.Lock не является.

1 Ответ

1 голос
/ 02 февраля 2020

Что происходит?

  • Когда создается асин c объект (asyncio.Lock()), он присоединяется к текущему событию l oop и может использоваться только с ним
  • Основной поток имеет некоторое текущее событие по умолчанию l oop (но другие создаваемые вами потоки не будут иметь события по умолчанию l oop)
  • asyncio.run() внутренне создает новое событие l oop, устанавливает его текущим и закрывает после завершения

Итак, вы пытаетесь использовать блокировку с событием l oop, отличным от того, к которому оно было присоединено создание. Это приводит к ошибкам.

И как я могу изменить этот код для работы?

Идеальное решение заключается в следующем:

import asyncio


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


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

Это будет гарантировать что каждый созданный асин c объект привязан к соответствующему событию l oop asyncio.run.

Выполнение события l oop (внутри asyncio.run) означает глобальную «точку входа» вашей программы asyn c.

Я бы хотел, чтобы ее можно было использовать и в другом месте.

Вы можете создать объект за пределами asyncio.run , но тогда вам следует переместить создание объекта asyn c из __init__ в другое место , чтобы asyncio.Lock() не создавалось до тех пор, пока asyncio.run () не будет вызвано .

Кроме того, можно ли использовать threading.Lock для вещей asyn c?

Нет , он используется для работы с потоками, в то время как asyncio работает с сопрограммами внутри один поток (обычно).

Это будет иметь дополнительное преимущество в том, чтобы быть потокобезопасным, о чем сообщает asyncio.Lock Тедли нет.

В asyncio обычно вам не нужны потоки, кроме main. Есть еще некоторые причины сделать это, но небезопасность потока asyncio.Lock не должна быть проблемой.


Подумайте о прочтении следующих ссылок. Это может помочь лучше понять ситуацию:

...