Python asyncio.sleep

Python asyncio.sleep

是否可以在等待的同时更新对象的状态(即成员变量的值)asyncio.sleep?

例如,假设您有一个服务器发送一个每秒递增的计数,客户端读取当前计数并将其存储在一个成员变量中。如果每mod 100 it asyncio.sleeps 10秒,sleep前后counter成员变量的值会不会变化?因此,例如在 100 点睡觉并在 110 点或仍然 100 点恢复?

代码示例:

server.py

import asyncio


async def send_counter(reader, writer):
    cnt = 0
    while True:
        print(f"send: {cnt!r}")

        # encode counter as little endian and send
        writer.write((cnt).to_bytes(4, "little"))
        await writer.drain()

        cnt += 1

        # pace the sends
        await asyncio.sleep(1)


async def main():
    server = await asyncio.start_server(send_counter, "127.0.0.1", 5555)

    async with server:
        await server.serve_forever()


asyncio.run(main())

client.py

import asyncio


class MyClass:
    def __init__(self):
        self._cnt = 0

    @property
    def count(self):
        return self._cnt

    @count.setter
    def count(self, x):
        self._cnt = x

    async def do_something_then_sleep(self):
        before = self._cnt

        # do something here

        print(f"sleeping for 10 seconds - count = {self._cnt}")
        await asyncio.sleep(10)

        # ideally self._cnt would reflect the latest count sent by the server
        if before != self._cnt:
            print(f"before and after different {before} != {self._cnt}")


async def tcp_echo_client():
    reader, _ = await asyncio.open_connection("127.0.0.1", 5555)

    o = MyClass()
    while True:
        data = await reader.read(4)
        o.count = int(int.from_bytes(data, "little"))
        print(f"Received: {o.count!r}")

        if (o.count % 10) == 9:
            await o.do_something_then_sleep()


asyncio.run(tcp_echo_client())

在上面的示例中,我认为问题是在等待 o.do_something_then_sleep 时我停止读取套接字,这是有道理的。理想情况下,我会在睡眠期间继续读取套接字,导致 before and after different 消息出现。

是否可以在单个线程上完成,避免引入线程、run_in_executor、新的事件循环和锁?也许 asyncio 中有一种机制可以用来自动读取套接字并用数据触发回调,避免显式读取?

是的! AP(Asynchronous Programming)本质上是“单线程并发”。这意味着 当一个协程正在休眠时,其他协程可以 运行.

但是请注意,与多线程不同,如果您不休眠(调用 await),则没有其他协程可以 运行 在两个语句之间,因此在我下面的示例中,变量不能从外部更改在两个打印语句之间。

import asyncio
import random

counter = 0

async def every_tenth_of_a_second():
    global counter
    while True:
        counter += 1
        await asyncio.sleep(.2 * random.random())

async def every_second():
    global counter
    while True:
        print('counter was', counter)
        counter += 1
        print('counter is now', counter)
        await asyncio.sleep(1)

async def async_main():
    await asyncio.wait([
        asyncio.create_task(every_tenth_of_a_second()),
        asyncio.create_task(every_second()),
    ])

asyncio.run(async_main())

这打印了:

counter was 1
counter is now 2
counter was 11
counter is now 12
counter was 20
counter is now 21
counter was 34
counter is now 35
counter was 44
counter is now 45
counter was 56
counter is now 57
counter was 69
counter is now 70
counter was 82
counter is now 83

我推荐 my blog post on AP 以获得进一步的解释! (: