【发布时间】:2016-07-18 23:56:43
【问题描述】:
我希望有人可以在这里帮助我。
我有一个对象,它能够具有返回协程对象的属性。这很好用,但是我有一种情况,当事件循环当前正在运行时,我需要从单独的线程中的同步代码中获取协程对象的结果。我想出的代码是:
def get_sync(self, key: str, default: typing.Any=None) -> typing.Any:
"""
Get an attribute synchronously and safely.
Note:
This does nothing special if an attribute is synchronous. It only
really has a use for asynchronous attributes. It processes
asynchronous attributes synchronously, blocking everything until
the attribute is processed. This helps when running SQL code that
cannot run asynchronously in coroutines.
Args:
key (str): The Config object's attribute name, as a string.
default (Any): The value to use if the Config object does not have
the given attribute. Defaults to None.
Returns:
Any: The vale of the Config object's attribute, or the default
value if the Config object does not have the given attribute.
"""
ret = self.get(key, default)
if asyncio.iscoroutine(ret):
if loop.is_running():
loop2 = asyncio.new_event_loop()
try:
ret = loop2.run_until_complete(ret)
finally:
loop2.close()
else:
ret = loop.run_until_complete(ret)
return ret
我正在寻找的是一种在多线程环境中同步获取协程对象结果的安全方法。 self.get() 可以返回一个协程对象,用于我设置的属性来提供它们。我发现的问题是:事件循环是否正在运行。在堆栈溢出和其他几个站点上搜索了几个小时后,我的(损坏的)解决方案就在上面。如果循环正在运行,我会创建一个新的事件循环并在新的事件循环中运行我的协程。这可行,只是代码永远挂在ret = loop2.run_until_complete(ret) 行上。
现在,我有以下结果:
-
self.get()的结果不是协程- 返回结果。 [好]
-
self.get()的结果是协程和事件循环未运行(基本上与事件循环在同一线程中)- 返回结果。 [好]
-
self.get()的结果是协程和事件循环正在运行(基本上在与事件循环不同的线程中)- 永远挂起等待结果。 [不好]
有谁知道我可以如何解决不好的结果,以便获得所需的价值?谢谢。
我希望我在这里有所了解。
我确实有充分且正当的理由使用线程;具体来说,我使用的是非异步的 SQLAlchemy,我将 SQLAlchemy 代码放到 ThreadPoolExecutor 中以安全地处理它。但是,我需要能够从这些线程中查询这些异步属性,以便 SQLAlchemy 代码安全地获取某些配置值。不,我不会为了完成我的需要而从 SQLAlchemy 切换到另一个系统,所以请不要提供替代方案。该项目进展太快,无法将如此基础的东西转换为它。
我尝试使用asyncio.run_coroutine_threadsafe() 和loop.call_soon_threadsafe() 都失败了。到目前为止,这已经取得了最大的成功,我觉得我只是错过了一些明显的东西。
如果有机会,我会编写一些代码来提供问题的示例。
好的,我实现了一个示例案例,它的工作方式符合我的预期。所以很可能我的问题在代码的其他地方。保持开放状态,如果需要,将更改问题以适应我的实际问题。
对于为什么来自asyncio.run_coroutine_threadsafe() 的concurrent.futures.Future 会永远挂起而不是返回结果,是否有人有任何可能的想法?
不幸的是,没有重复我的错误的示例代码如下:
import asyncio
import typing
loop = asyncio.get_event_loop()
class ConfigSimpleAttr:
__slots__ = ('value', '_is_async')
def __init__(
self,
value: typing.Any,
is_async: bool=False
):
self.value = value
self._is_async = is_async
async def _get_async(self):
return self.value
def __get__(self, inst, cls):
if self._is_async and loop.is_running():
return self._get_async()
else:
return self.value
class BaseConfig:
__slots__ = ()
attr1 = ConfigSimpleAttr(10, True)
attr2 = ConfigSimpleAttr(20, True)
def get(self, key: str, default: typing.Any=None) -> typing.Any:
return getattr(self, key, default)
def get_sync(self, key: str, default: typing.Any=None) -> typing.Any:
ret = self.get(key, default)
if asyncio.iscoroutine(ret):
if loop.is_running():
fut = asyncio.run_coroutine_threadsafe(ret, loop)
print(fut, fut.running())
ret = fut.result()
else:
ret = loop.run_until_complete(ret)
return ret
config = BaseConfig()
def example_func():
return config.get_sync('attr1')
async def main():
a1 = await loop.run_in_executor(None, example_func)
a2 = await config.attr2
val = a1 + a2
print('{a1} + {a2} = {val}'.format(a1=a1, a2=a2, val=val))
return val
loop.run_until_complete(main())
这是我的代码正在执行的操作的精简版本,并且示例有效,即使我的实际应用程序没有。我被困在哪里寻找答案。欢迎提出关于在哪里尝试追踪我的“永远卡住”问题的建议,即使我上面的代码实际上并没有重复该问题。
【问题讨论】:
标签: multithreading python-3.x coroutine python-asyncio event-loop