Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Python 3.5 asyncio在不同线程中的同步代码的事件循环上执行协同例程_Multithreading_Python 3.x_Coroutine_Python Asyncio_Event Loop - Fatal编程技术网

Multithreading Python 3.5 asyncio在不同线程中的同步代码的事件循环上执行协同例程

Multithreading Python 3.5 asyncio在不同线程中的同步代码的事件循环上执行协同例程,multithreading,python-3.x,coroutine,python-asyncio,event-loop,Multithreading,Python 3.x,Coroutine,Python Asyncio,Event Loop,我希望有人能在这里帮助我 我有一个对象,它具有返回协同路由对象的属性。这工作得很好,但是我有一种情况,当事件循环当前正在运行时,我需要在一个单独的线程中从同步代码中获取协程对象的结果。我想到的代码是: def get_sync(self, key: str, default: typing.Any=None) -> typing.Any: """ Get an attribute synchronously and safely. Note: Th

我希望有人能在这里帮助我

我有一个对象,它具有返回协同路由对象的属性。这工作得很好,但是我有一种情况,当事件循环当前正在运行时,我需要在一个单独的线程中从同步代码中获取协程对象的结果。我想到的代码是:

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.运行\u直到完成(ret)

现在,我有以下场景和结果:

  • self.get()
    的结果不是协同程序
    • 返回结果[好]
  • self.get()
    的结果是一个协同程序&事件循环未运行(基本上与事件循环在同一线程中)
    • 返回结果[好]
  • self.get()的结果是一个协同程序&事件循环正在运行(基本上在与事件循环不同的线程中)
    • 永远等待结果[坏]
  • 有没有人知道我如何着手修复坏结果,以便获得所需的价值?谢谢

    我希望我在这里讲得有道理

    我确实有一个好的、有效的理由使用线程;具体来说,我使用的是非异步的SQLAlchemy,我将SQLAlchemy代码推送到ThreadPoolExecutor以安全地处理它。但是,我需要能够从这些线程中查询这些异步属性,以便SQLAlchemy代码安全地获得某些配置值。不,我不会为了完成我需要的东西而从SQLAlchemy切换到另一个系统,所以请不要提供它的替代方案。这个项目进展得太远了,无法将如此基础的东西转变为它

    我尝试使用
    asyncio.run\u coroutine\u threadsafe()
    loop.call\u soon\u threadsafe()
    ,但都失败了。到目前为止,这是最有效的,我觉得我只是错过了一些明显的东西

    如果有机会,我将编写一些代码来提供问题的示例

    好的,我实现了一个示例案例,它按照我预期的方式工作。所以我的问题可能在代码的其他地方。如果我需要的话,把这个问题留着,我会根据我的实际问题来改变这个问题

    关于为什么来自
    asyncio.run\u coroutine\u 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())
    

    这是我的代码的精简版本,即使我的实际应用程序不这样做,这个示例也能工作。我被困在哪里寻找答案。欢迎您就在何处尝试跟踪我的“永远卡住”问题提出建议,即使我上面的代码实际上没有复制该问题。

    您不太可能需要同时运行多个事件循环,因此这部分看起来非常错误:

        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)
    
    即使测试循环是否正在运行,也似乎不是正确的方法。最好明确地将(唯一的)运行循环指定给
    get\u sync
    ,并使用以下方法安排协同路由:



    编辑:挂起问题可能与在错误循环中计划的任务有关(例如,调用协同例程时忘记可选的
    循环
    参数)。使用(现在已合并)调试此类问题应该更容易:当循环和未来不匹配时,会引发
    运行时错误。因此,您可能希望使用最新版本的asyncio运行测试。

    好的,我通过采用不同的方法使代码正常工作。这个问题与使用具有文件IO的东西有关,我正在使用loop.run_in_executor()对文件IO组件将其转换为协同程序。然后,我尝试在一个从另一个线程调用的同步函数中使用它,使用另一个循环进行处理。这在我的代码中是一个非常重要的例程(在执行短时间运行的代码时,调用次数可能会超过一百万次),我决定我的逻辑变得太复杂了。所以我把它简单化了。现在,如果我想异步使用文件IO组件,我会显式地使用我的“get\u async()”方法,否则,我会通过正常的属性访问使用我的属性


    通过消除我的逻辑复杂性,它使代码更清晰、更容易理解,更重要的是,它实际上可以工作。虽然我不是100%确定我知道问题的根本原因(我相信这与处理属性的线程有关,然后该线程又启动另一个线程,在处理属性之前尝试读取该属性,这会导致类似竞争条件的情况并停止我的代码,但不幸的是,我无法在我的应用程序外复制该错误以完全证明它),我能够克服它并继续我的开发工作。

    是的,换了它,得到了完全相同的问题。我现在正计划采取一种不同的方法来解决它。我认为我已经更好地解决了这个问题,并希望有一个更好的例子,如果我能够复制错误,那将是非常好的。问题是这个是一个执行数百次的程序,需要一段时间
    def get_sync(self, key, loop):
        ret = self.get(key, default)
        if not asyncio.iscoroutine(ret):
            return ret
        future = asyncio.run_coroutine_threadsafe(ret, loop)
        return future.result()