【问题标题】:Python asyncio wait for threadsPython asyncio等待线程
【发布时间】:2020-10-12 10:00:25
【问题描述】:

我有一个“服务器”线程,它应该监听来自其他服务器线程的调用/事件,同时执行一些其他代码。最近我在 Node.js 上工作了很多,所以我认为使用 async/await 创建一个事件循环会很好,我可以在其中等待其他线程加入事件循环并在它们最终加入时处理它们的响应。

为了测试这个想法,我在 Python 3.5 中编写了以下测试脚本:

# http://stackabuse.com/python-async-await-tutorial/
# Testing out Python's asynchronous features
import asyncio
from time import sleep
import threading
from threading import Thread
import random

class MyThread(Thread):

    def __init__(self, message):
        Thread.__init__(self)
        self._message = message

    def run(self):
        self._return = self._message + " oli viesti"
        a = random.randint(1, 5)
        print("Sleep for ", a)
        sleep(a)
        print("Thread exiting...")


    def join(self):
        Thread.join(self)
        return self._return



async def send(message):
    t = MyThread(message)  # daemon = True
    t.start()
    print("asd")
    return t.join()

async def sendmsg(msg):
    response = await send(msg)
    print("response is ", response)


if __name__ == "__main__":
    # Initiate a new thread and pass in keyword argument dictionary as parameters
    loop = asyncio.get_event_loop()
    tasks = [
        asyncio.ensure_future(sendmsg("hippa1"), loop=loop),
        asyncio.ensure_future(sendmsg("hippa2"), loop=loop),
        asyncio.ensure_future(sendmsg("hippa3"), loop=loop)
    ]

    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()

在示例中,我想用不同的字符串启动三个工作线程并等待它们完成。工人睡眠的时间是随机的,所以我希望他们在脚本多次运行时以随机顺序完成。事实证明,它们似乎是按顺序执行的,第二个线程在第一个线程之后开始。

我的错误是什么? sleep 不应该只阻塞它所在的线程吗?我的事件循环设置正确吗?我可以异步/等待加入吗?

最终我想向其他线程发送消息并等待它们的响应,然后运行带有返回值的回调函数。

编辑:为了澄清,最终我想在我的主线程中等待带有 async/await 的条件变量并运行其他代码,直到一些条件变量让执行通过。在这个示例代码中,我试图对工作线程的连接做同样的事情。

【问题讨论】:

  • time.sleep 不是异步的,试试await asyncio.sleep
  • 但是它是在一个单独的线程中触发的,所以它不应该只是阻塞单独的线程而不是事件循环所在的主线程吗?我的理解是睡眠是线程,而不是进程阻塞。那么,即使使用 async/await 结构,为什么 join 也会阻塞我的主线程呢?

标签: python asynchronous async-await python-multithreading


【解决方案1】:

最终,由于这段代码,它是按顺序运行的:

async def send(message):
    t = MyThread(message)  # daemon = True
    t.start()
    print("asd")
    return t.join()

您启动一个线程,然后在继续之前立即等待该线程完成。这就是它们按顺序执行的原因。

Node.js 和 asyncio 不一定要创建新线程来执行它们的操作。例如,Node.js 只使用单个线程,但它使用内核级别的函数(例如 'epoll' )来调用您在某些新网络活动发生时指示的回调。这允许单个线程管理数百个网络连接。

这就是为什么当您在没有 Thread 实例的情况下执行此操作时,您可能会在当前运行的线程上调用 sleep,这与主线程相同。当您将 asyncio 与网络功能一起使用时,您可以使用“yield from”结构,这允许其他代码块在其他任务与其他远程服务执行操作时执行。

主要结构是正确的。你想要这段代码:

loop.run_until_complete(asyncio.wait(tasks))

但是不要依赖'sleep'来测试功能,需要进行网络调用,或者使用:

yield from asyncio.sleep(1)

在这种情况下,无需启动单独的线程。

【讨论】:

  • 关于线程启动的好点。但是,就我而言,我确实需要单独的线程,但如果可能,我想异步等待来自它们的条件。所以我想要一个函数来检查事件循环中的加入,但如果仍然执行其他任意代码,直到加入在循环中解除阻塞。所以我的情况并不完全类似于 Node.js,而是我想等待其他线程完成/设置条件变量,而不会通过使用异步事件循环阻塞我的主线程。
  • 简而言之,我不知道怎么写:yield t.join()
  • 在这种情况下,我建议您根本不要使用 asyncio,而是使用中间的队列。您可以使用 task_done() 在队列中等待并使用 get() 来获取工作项。然后,您可以使用另一个队列将结果传回主线程。在文档页面上有一个代码示例:docs.python.org/3/library/queue.html
  • 很高兴知道。实际上,当我无法让 asyncio 工作时,我终于以这种方式实现了它。架构变得足够干净,可以与之合作。
猜你喜欢
  • 2020-07-03
  • 2020-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-26
  • 2022-11-19
  • 2020-10-07
相关资源
最近更新 更多