运行 自己线程中的事件循环
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
我正在玩 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