Python异步IO运行\u协同程序\u线程安全从未运行协同程序?

Python异步IO运行\u协同程序\u线程安全从未运行协同程序?,python,python-multithreading,python-asyncio,Python,Python Multithreading,Python Asyncio,我不确定我在这里做错了什么,我正在尝试创建一个类,该类包含一个队列,并使用一个协程来消费该队列上的项目。问题在于事件循环正在一个单独的线程中运行(在该线程中,我执行loop.run\u forever()以使其运行) 但我看到的是,消费物品的协同程序从未启动: import asyncio from threading import Thread import functools # so print always flushes to stdout print = functools.pa

我不确定我在这里做错了什么,我正在尝试创建一个类,该类包含一个队列,并使用一个协程来消费该队列上的项目。问题在于事件循环正在一个单独的线程中运行(在该线程中,我执行
loop.run\u forever()
以使其运行)

但我看到的是,消费物品的协同程序从未启动:

import asyncio
from threading import Thread

import functools

# so print always flushes to stdout
print = functools.partial(print, flush=True)


def start_loop(loop):
    def run_forever(loop):
        print("Setting loop to run forever")
        asyncio.set_event_loop(loop)
        loop.run_forever()
        print("Leaving run forever")

    asyncio.set_event_loop(loop)
    print("Spawaning thread")
    thread = Thread(target=run_forever, args=(loop,))
    thread.start()


class Foo:
    def __init__(self, loop):
        print("in foo init")
        self.queue = asyncio.Queue()
        asyncio.run_coroutine_threadsafe(self.consumer(self.queue), loop)

    async def consumer(self, queue):
        print("In consumer")
        while True:
            message = await queue.get()
            print(f"Got message {message}")
            if message == "END OF QUEUE":
                print(f"exiting consumer")
                break
            print(f"Processing {message}...")


def main():
    loop = asyncio.new_event_loop()
    start_loop(loop)
    f = Foo(loop)
    f.queue.put("this is a message")
    f.queue.put("END OF QUEUE")

    loop.call_soon_threadsafe(loop.stop)

    # wait for the stop to propagate and complete
    while loop.is_running():
        pass


if __name__ == "__main__":
    main()
输出:

Spawaning thread Setting loop to run forever in foo init Leaving run forever 剥落线 将循环设置为永远运行 在foo init中 永远离开
这段代码有几个问题

首先,检查警告:

test.py:44: RuntimeWarning: coroutine 'Queue.put' was never awaited
f.queue.put("this is a message")
test.py:45: RuntimeWarning: coroutine 'Queue.put' was never awaited
f.queue.put("END OF QUEUE")
这意味着它是一个协同程序,因此必须使用:

您还可以使用同步方法。但是,asyncio对象通常不是线程安全的,因此每个同步调用都必须经过:

另一个问题是循环在使用者任务开始处理项目之前停止。您可以向
Foo
类添加
join
方法,以等待使用者完成:

class Foo:
    def __init__(self, loop):
        [...]
        self.future = asyncio.run_coroutine_threadsafe(self.consumer(self.queue), loop)

    def join(self):
        self.future.result()
然后确保在停止循环之前调用此方法:

f.join()
loop.call_soon_threadsafe(loop.stop)

这应该足以让程序按预期工作。然而,该代码在几个方面仍然存在问题

首先,不应该在主线程和额外线程中都设置循环。异步IO循环并不意味着在线程之间共享,因此您需要确保所有与异步IO相关的事情都发生在专用线程中

由于
Foo
负责这两个线程之间的通信,因此必须格外小心,以确保每一行代码都在正确的线程中运行。例如,
asyncio.Queue
的实例化必须在asyncio线程中进行

有关程序的更正版本,请参阅


另外,我想指出,这不是asyncio的典型用例。通常,您希望在主线程中运行asyncio循环,特别是在需要:

asyncio支持从不同的线程运行子进程,但有以下限制:

  • 事件循环必须在主线程中运行
  • 在从其他线程执行子进程之前,必须在主线程中实例化子监视程序。在主线程中调用get_child_watcher()函数来实例化子观察程序
我建议以另一种方式设计应用程序,即在主线程中运行asyncio并用于同步阻塞代码

class Foo:
    def __init__(self, loop):
        [...]
        self.future = asyncio.run_coroutine_threadsafe(self.consumer(self.queue), loop)

    def join(self):
        self.future.result()
f.join()
loop.call_soon_threadsafe(loop.stop)