运行 自己线程中的事件循环

Running an event loop within its own thread

我正在玩 Python 的新(大概)asyncio 东西,试图将其事件循环与传统线程结合起来。我写了一个 class 在它自己的线程中运行事件循环,以隔离它,然后提供一个(同步)方法在该循环上运行协程并 returns 结果。 (我意识到这使它成为一个有点毫无意义的例子,因为它必然会序列化所有内容,但它只是作为概念验证)。

import asyncio
import aiohttp
from threading import Thread


class Fetcher(object):
    def __init__(self):
        self._loop = asyncio.new_event_loop()
        # FIXME Do I need this? It works either way...
        #asyncio.set_event_loop(self._loop)

        self._session = aiohttp.ClientSession(loop=self._loop)

        self._thread = Thread(target=self._loop.run_forever)
        self._thread.start()

    def __enter__(self):
        return self

    def __exit__(self, *e):
        self._session.close()
        self._loop.call_soon_threadsafe(self._loop.stop)
        self._thread.join()
        self._loop.close()

    def __call__(self, url:str) -> str:
        # FIXME Can I not get a future from some method of the loop?
        future = asyncio.run_coroutine_threadsafe(self._get_response(url), self._loop)
        return future.result()

    async def _get_response(self, url:str) -> str:
        async with self._session.get(url) as response:
            assert response.status == 200
            return await response.text()


if __name__ == "__main__":
    with Fetcher() as fetcher:
        while True:
            x = input("> ")

            if x.lower() == "exit":
                break

            try:
                print(fetcher(x))
            except Exception as e:
                print(f"WTF? {e.__class__.__name__}")

为避免这听起来太像一个 "Code Review" 问题,asynchio.set_event_loop 的目的是什么?我是否需要在上面使用它?无论有无,它都可以正常工作。此外,是否有循环级别的方法来调用协程和 return 未来?用模块级函数来做这件事似乎有点奇怪。

如果您在任何地方调用 get_event_loop 并希望它进入 return 您调用 new_event_loop 时创建的循环,则需要使用 set_event_loop

来自docs

If there’s need to set this loop as the event loop for the current context, set_event_loop() must be called explicitly.

由于您没有在示例中的任何地方调用 get_event_loop,因此可以省略对 set_event_loop 的调用。

我可能误解了,但我认为@dirn 在标记的答案中的评论在说明 get_event_loop 从线程工作时是不正确的。请参阅以下示例:

import asyncio
import threading

async def hello():
    print('started hello')
    await asyncio.sleep(5)
    print('finished hello')

def threaded_func():
    el = asyncio.get_event_loop()
    el.run_until_complete(hello())

thread = threading.Thread(target=threaded_func)
thread.start()

这会产生以下错误:

RuntimeError: There is no current event loop in thread 'Thread-1'.

可以通过以下方式修复:

 - el = asyncio.get_event_loop()
 + el = asyncio.new_event_loop()

documentation 还指定此技巧(通过调用 get_event_loop 创建事件循环)仅适用于主线程:

If there is no current event loop set in the current OS thread, the OS thread is main, and set_event_loop() has not yet been called, asyncio will create a new event loop and set it as the current one.

最后,如果您使用的是 3.7 或更高版本

,文档还建议使用 get_running_loop 而不是 get_event_loop