如何在 asyncio 中暂停和恢复任务?

How can I pause & resume a task in asyncio?

假设我有一个循环遍历现有订单的后台任务(必要时进行一些操作)。 现在,如果我想添加订单,这个后台任务应该在我下新订单时停止,并在我完成后恢复。

在伪代码中:

async def loop_orders():
    while True:
        do_this()
        do_that()
    return

async def create_order():
    stop_loop_orders()
        ...
        send_order()
    resume_loop_orders()
    return

我无法弄清楚这两个中的哪一个是要走的路:

我的第一个想法是

lock = asyncio.Lock()

async def loop_orders():
    while True:
        if not lock.locked():
            do_this()
            do_that()

async def create_order():
    async with lock:
        send_order()

所以在发送订单之前它设置 lock 所以 if not lock.locked(): 应该跳过 do_this() do_that()。但是代码可以 运行 在 do_this() do_that() 的某处然后 lock.locked() 无法阻止它。所以也许它应该在两者中

lock = asyncio.Lock()

async def loop_orders():
    while True:
        async with lock:
            do_this()
            do_that()

async def create_order():
    async with lock:
        send_order()

这样当它开始发送时它必须等待do_this() do_that()。当它开始时 do_this() do_that() 然后它必须等待 send_order()


顺便说一句:

如果你想理解它,那么在普通代码中它可能看起来像

lock = False

async def loop_orders():
    global lock

    while True:

        # wait if `lock` was set `True` in other function(s)
        while lock:         
            pass

        # set `True` to block other function(s)
        lock = True

        do_this()
        do_that()

        # set `False` to unblock other function(s)
        lock = False

async def create_order():
    global lock

    # wait if `lock` was set `True` in other function(s)
    while lock:
        pass

    # set `True` to block other function(s)
    lock = True

    send_order()

    # set `False` to unblock other function(s)
    lock = False

顺便说一句:

我认为您可以以类似的方式使用 Event,但您应该以不同的方式思考:您可以使用 wait if the event is **unset**/**clear**

而不是 wait if the event is set

您不能暂停和恢复异步任务。

您可以取消任务并稍后创建一个新任务,但这导致的问题多于解决的问题。数据一致性可能会受到影响,新任务将不会在前一个任务中断的地方恢复。

您可以轻松地让任务在某个特定点(或多个点)等待

async def loop_orders():
    while True:
        ... wait here while paused ...
        do_this()
        do_that()

但是当 create_order 暂停 loop_orders 任务时,前者必须等到后者到达它暂停的那个点 - create_order 任务请求暂停并且 loop_orders 确认。

我用两个 Events 做了一个小演示,我将它们命名为 enableidle,试图使方法名称为 .clear.set并且 .wait 符合逻辑。

import asyncio

enable = None
idle = None

async def loop_orders():
    while True:
        idle.set()
        await enable.wait()
        idle.clear();
        print("processing order ... ", end="", flush=True)
        await asyncio.sleep(0.7)
        print("done")

async def create_order():
    enable.clear();
    await idle.wait()
    print("-- pause start ... ", end="", flush=True)
    await asyncio.sleep(2)
    print("stop")
    enable.set()

async def test():
    global enable, idle

    enable = asyncio.Event()
    enable.set()    # initial state = enabled
    idle = asyncio.Event()

    asyncio.create_task(loop_orders())
    await asyncio.sleep(2)
    await create_order()
    await asyncio.sleep(2)
    await create_order()
    await asyncio.sleep(1)
    print("EXIT")


asyncio.run(test())

首先,我认为您或许应该考虑使用队列将其实现为 producer/consumer 模式。

但是如果你真的想暂停执行一个Task,你可以看看这个