在三重奏中,我如何才能拥有与我的对象一样长的后台任务?

lil*_*jwg 19 python async-await python-trio

我正在写一个会在其生命周期中产生任务的类.因为我正在使用Trio,所以我不能在没有托儿所的情况下产生任务.我的第一个想法是self._nursery在我的课堂上有一个我可以将任务产生的东西.但似乎托儿所对象只能在上下文管理器中使用,因此它们始终在创建它们的同一范围内关闭.我不想从外面传递托儿所,因为它是一个实现细节,但我确实希望我的对象能够产生与对象一样长的任务(例如心跳任务).

我如何使用Trio编写这样一个具有长期后台任务的类?

Nat*_*ith 22

好问题!

Trio最奇怪,最有争议的决定之一是,它认为后台任务的存在不是实现细节,应该作为API的一部分公开.总的来说,我认为这是正确的决定,但它肯定有点实验性,并有一些权衡取舍.

为什么Trio会这样做?根据我的经验,其他系统似乎可以抽象出后台任务或线程的存在,但实际上它会以各种方式泄漏:它们最终破坏了控制-C处理,或者当你'时它们会导致问题'我试图干净地退出程序,或者当你试图取消主操作时它们会泄漏,或者你因为你调用的函数完成但你承诺要完成的工作仍然在后台或后台任务中完成而导致排序问题遇到意外的异常崩溃,然后异常就会丢失,并且会出现各种奇怪的问题......所以虽然它可能会让你的API在短期内感觉有点混乱,但从长远来看,如果你明白这一点,一切都会变得更容易.

另外,请记住,编写和使用Trio库的其他人都有同样的问题,因此您的API不会感觉太奇怪:-).

我不知道你究竟要做什么.也许它就像一个websocket连接,你想要不断地从套接字读取以响应心跳("ping")请求.一种模式是做类似的事情:

@asynccontextmanager
def open_websocket(url):
    ws = WebSocket()
    await ws._connect(url)
    try:
        async with trio.open_nursery() as nursery:
            nursery.start_soon(ws._heartbeat_task)
            yield ws
            # Cancel the heartbeat task, since we're about to close the connection
            nursery.cancel_scope.cancel()
    finally:
        await ws.aclose()
Run Code Online (Sandbox Code Playgroud)

然后您的用户可以使用它:

async with open_websocket("https://...") as ws:
    await ws.send("hello")
    ...
Run Code Online (Sandbox Code Playgroud)

如果你想获得更好的选择,另一种选择是提供一个版本,用户可以在自己的托儿所中传递,专家:

class WebSocket(trio.abc.AsyncResource):
    def __init__(self, nursery, url):
        self._nursery = nursery
        self.url = url

    async def connect(self):
        # set up the connection
        ...
        # start the heartbeat task
        self._nursery.start_soon(self._heartbeat_task)

    async def aclose(self):
        # you'll need some way to shut down the heartbeat task here
        ...
Run Code Online (Sandbox Code Playgroud)

然后还为那些只想要一个连接并且不想让托儿所弄乱的人提供便利API:

@asynccontextmanager
async def open_websocket(url):
    async with trio.open_nursery() as nursery:
        async with WebSocket(nursery, url) as ws:
            await ws.connect()
            yield ws
Run Code Online (Sandbox Code Playgroud)

托管传递方法的主要优点是,如果您的用户想要打开大量的websocket连接,任意数量的websocket连接,那么他们可以在websocket管理代码的顶部打开一个托儿所,并且然后里面有很多的websockets.

你可能想知道:你在哪里找到这个@asynccontextmanager?嗯,它包含在3.7中的stdlib中,但是它还没有出现,所以取决于你何时阅读它,你可能还没有使用它.在此之前,该async_generator软件包可以让您@asynccontextmanager一直回到3.5.