Почему get_event_l oop нельзя использовать с run_in_executor - PullRequest
0 голосов
/ 16 марта 2020

Может кто-нибудь сказать, почему код ниже

import asyncio
import time
from concurrent.futures import ThreadPoolExecutor

ASYNC_INTERVAL = 0.1

def plain_hello_world(name):
    s = "Hello world "+str(name)
    print(s)
    return s

def plain_loop(name, t):
    start_time = time.time()
    prev_time = start_time
    while (time.time() - start_time < t):
        if time.time() - prev_time > ASYNC_INTERVAL:
            prev_time = time.time()
            plain_hello_world(name)

def task1():
    loop = asyncio.get_event_loop()
    task = loop.run_in_executor(None, plain_loop, "func", 1)
    loop.run_until_complete(task)

def task2():
    loop = asyncio.get_event_loop()
    task = loop.run_in_executor(None, task1)
    loop.run_until_complete(task)

if __name__ == "__main__":
    task2()

получает ошибку ниже:

Traceback (most recent call last):
  File "asyncio_practice4.py", line 28, in <module>
    task2()
  File "asyncio_practice4.py", line 25, in task2
    loop.run_until_complete(task)
  File "/usr/lib/python3.6/asyncio/base_events.py", line 484, in run_until_complete
    return future.result()
  File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
    result = self.fn(*self.args, **self.kwargs)
  File "asyncio_practice4.py", line 18, in task1
    loop = asyncio.get_event_loop()
  File "/usr/lib/python3.6/asyncio/events.py", line 694, in get_event_loop
    return get_event_loop_policy().get_event_loop()
  File "/usr/lib/python3.6/asyncio/events.py", line 602, in get_event_loop
    % threading.current_thread().name)
RuntimeError: There is no current event loop in thread 'ThreadPoolExecutor-0_0'.

Вопрос:

Я не понимаю почему возникает такая ошибка.

Запуск task1() только в порядке, но вместе с другим run_in_executor() он говорит, что в настоящее время нет l oop. (Но я думаю, что должно, я не создал новую тему)

Кто-нибудь знает, что происходит?

И как это исправить, предполагая, что мы можем работать только на task2()?

Примечание:

Причина вызова 2 run_in_executor() состоит в том, что приведенный выше код имитирует интеграцию третьей библиотеки в асинхронный код.

plain_hello_world(), plain_loop() и task1() - это коды в lib, которые я не могу изменить.

Предполагается, что task1() работает в течение 100 с, и я не хочу ждать его, поэтому я пытаюсь запустить его в executor как то, как другие простые функции работают с asyncio.

Редактировать: Основываясь на ответе, вот ревизия, которая работает:

def task2():
    loop = asyncio.get_event_loop()
    def wrapper():
        asyncio.set_event_loop(asyncio.new_event_loop())
        task1()
    task = loop.run_in_executor(None, wrapper)
    loop.run_until_complete(task)

Хотя я не уверен насколько это "правильно" или хорошо.

1 Ответ

1 голос
/ 16 марта 2020

Я не понимаю, почему возникает такая ошибка.

Это происходит потому, что task1 предполагает, что он будет работать в основном потоке (где get_event_loop() создает событие l oop по требованию) или в потоке, где set_event_loop ранее вызывался для установки события l oop. Так как run_in_executor вызывает свою функцию в потоке, отличном от основного потока, и ваш код не вызывает set_event_loop() перед вызовом, вы получаете ошибку.

Предполагается, что task1 () выполняется для 100 и я не хочу ждать его, поэтому я пытаюсь запустить его в executor, так как другие простые функции работают с asyncio.

Запуск функции syn c и ее ожидание не выполняются. не то, что вы делаете с Asyncio, это работа для обычных тем. Например:

def task1_bg():
    def work():
        asyncio.set_event_loop(asyncio.new_event_loop())
        task1()
    threading.Thread(target=work).start()

if __name__ == '__main__':
    task1_bg()
    # do something else...
...