You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过__await__魔术方法封装线程/进程以创建可等待类?

如何基于基础生成器实现可等待(awaitable)类实例?

我希望创建**可等待(awaitable)**类实例,这在使用没有asyncio包装器的数据库连接器时会很有用,同时也有助于加深对asyncio和生成器(generators)的理解。我提出了以下两种解决方案:

方案1

import asyncio
import threading
import time


async def ticker():
    """Just some kind of async ticker function."""
    while True:
        await asyncio.sleep(1)
        print("tick tack sleep...")


class ThreadAwaitableWithToThread:
    """Illustrates how to wrap Thread in awaitable object."""
    def do_work(self):
        """Some target function for another Thread."""
        cur_th_name = threading.current_thread().getName()
        for i in range(5):
            print(f"Do work-{i} in {cur_th_name}")
            time.sleep(2)

        return "well done result!"

    def __await__(self):
        """Magic method to create awaitable object."""
        # to_thread is wrapper around run_in_executor
        result = yield from asyncio.to_thread(self.do_work).__await__()
        # The same without asyncio.to_thread:
        # result = yield from loop.run_in_executor(None, self.do_work).__await__()
        return result


async def amain():
    """Main asyncio entrypoint of the app."""
    t = asyncio.create_task(ticker())  # "daemon" task
    res = await ThreadAwaitableWithToThread()
    t.cancel()  # cancel "daemon" task
    print(f"Result: {res}")

if __name__ == '__main__':
    asyncio.run(amain())

该方案基于loop.run_in_executor,可能是最优解,但我希望找到基于基础生成器而非asyncio库功能的实现方式。

方案2

import asyncio
import threading
import time


async def ticker():
    """Just some kind of async ticker function."""
    while True:
        await asyncio.sleep(1)
        print("tick tack sleep...")


class ThreadAwaitable:
    """Illustrates how to wrap Thread in awaitable object."""

    def __init__(self):
        self.done = threading.Event()  # sync between this and another thread

    def do_work(self):
        """Some target function for another Thread."""
        cur_th_name = threading.current_thread().getName()
        for i in range(5):
            print(f"Do work-{i} in {cur_th_name}")
            time.sleep(2)

        self.done.set()

    def __await__(self):
        """Magic method to create awaitable object."""
        th = threading.Thread(target=self.do_work)
        th.start()  # start another tread
        while True:
            if self.done.is_set():  # sync with another thread
                print(f"Work in {self.__class__.__name__} is done!")
                return "well done result!"

            # make loop switch between tasks while waiting another Thread to finish work.
            yield from asyncio.sleep(0.1).__await__()


async def amain():
    """Main asyncio entrypoint of the app."""
    t = asyncio.create_task(ticker())  # "daemon" task
    res = await ThreadAwaitable()
    t.cancel()  # cancel "daemon" task
    print(f"Result: {res}")


if __name__ == '__main__':
    asyncio.run(amain())

该方案基于借助asyncio.sleep实现的长轮询,会消耗额外资源。

我希望找到更优雅的解决方案,了解到select、selectors库可监听套接字,若将线程/进程与套接字绑定,可在内部通过套接字切换协程/生成器,但相关库文档不够详细,且这相当于自行实现asyncio(类似David Beazley在Pycon 2015的分享)。请问是否有更好的解决方案?


内容的提问来源于stack exchange,提问作者Artiom Kozyrev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 08:30:41