我遗漏了一些小的,但非常关键的一点,为什么在Python中的并发性是非阻塞的. 我知道有一个调度器,它管理异步功能,并且并发性是协作的:异步功能应该将控制权交还给调度器,使其不会挂起(即非阻塞).

例如,我不明白的是,为什么asyncio.sleep()是"非阻塞".如果它正在执行,则不能运行任何其他代码.请考虑以下示例:

import asyncio
from time import perf_counter


async def main():
    # case A
    start = perf_counter()
    await asyncio.sleep(5) # task #1
    await asyncio.sleep(5) # task #2
    print(perf_counter() - start) # 10

    # case B
    start = perf_counter()
    await asyncio.gather(*(
        asyncio.sleep(5), asyncio.sleep(5)
    ))
    print(perf_counter() - start) # 5

if __name__ == "__main__":
    asyncio.run(main())

为什么调度程序调度 case B中的所有任务,使它们同时运行,而不调度 case A中的任务? task #1不是"阻塞"了程序的执行吗?(因为除了等待task #1完成外,你什么也做不了)

在上面的例子中,我希望task #1返回控制,这样task #2将开始执行,但这并没有发生……为什么?因为循环忙于执行task #1,所以无法开始执行task #2?为什么gather()没有发生同样的事情呢?

推荐答案

taskcoroutine是不同的.你启动一个协程,你的main作为你开始时唯一的任务.任务是计划在事件循环中与当前任务并发运行的内容.

asyncio.sleep()不是一项任务,it is a coroutine.你await号这条协奏曲.这告诉事件循环,在sleep个协同程序完成之前,周围的协同程序(main)不能继续.

由于您显式地阻止了第一个sleepmain协同 routine ,因此第二个sleep将在第一个完成后启动.

sleep协程实际上并不会使线程进入Hibernate 状态.相反,它会与事件循环通信,表明它被阻止,直到经过了一定时间.如果事件循环包含任何其他未被阻止的任务,它将同时处理这些任务.否则,事件循环将阻塞并等待,直到最早的超时发生.

这第二部分解释了await asyncio.gather(*(asyncio.sleep(5), asyncio.sleep(5)))年发生的事情.gather将所有给定参数调度为并发任务.因此,两个睡眠同时开始,因此它们同时结束.

可以将事件循环看作三组对象:一组当前可以运行的任务、一组被阻止的任务以及一组可以等待以将被阻止的任务更改为可运行任务的事件.await asyncio.sleep()只是通知事件循环当前任务被阻止,直到发生合适的超时事件.

跟进问题

"如果事件循环包含任何其他未被阻塞的任务,它将同时处理这些任务"--您能用代码提供这样一个例子吗?

这有三项任务.Two将定期打印消息10次,直到取消.主任务在超时时阻塞,并停止其他任务.

import asyncio


async def say(message):
    while True:
        print(message)
        try:
            await asyncio.sleep(1)
        except asyncio.CancelledError:
            return


async def main():
    task1 = asyncio.create_task(say("Hello"))
    task2 = asyncio.create_task(say("handsome"))
    await asyncio.sleep(10)
    task1.cancel()
    task2.cancel()
    await asyncio.gather(task1, task2)


if __name__ == "__main__":
    asyncio.run(main())

更常见的用例是一台TCP网络服务器.主任务在侦听套接字上等待.每个新的客户端连接都会变成一个新任务.每个任务都使用Asyncio来等待传入的数据或将数据传输到客户端而不会阻塞.只要套接字被解锁,事件循环就会在任务之间自动切换.

Python相关问答推荐

Pandas :多索引组

跟踪我已从数组中 Select 的样本的最有效方法

如何调整spaCy token 化器,以便在德国模型中将数字拆分为行末端的点

Python多处理:当我在一个巨大的pandas数据框架上启动许多进程时,程序就会陷入困境

处理(潜在)不断增长的任务队列的并行/并行方法

追溯(最近最后一次调用):文件C:\Users\Diplom/PycharmProject\Yolo01\Roboflow-4.py,第4行,在模块导入roboflow中

有没有一种方法可以从python的pussompy比较结果中提取文本?

当我try 在django中更新模型时,模型表单数据不可见

名为__main__. py的Python模块在导入时不运行'

在www.example.com中使用`package_data`包含不包含__init__. py的非Python文件

通过ManyToMany字段与Through在Django Admin中过滤

如何更改groupby作用域以找到满足掩码条件的第一个值?

如何使用OpenGL使球体遵循Python中的八样路径?

Polars map_使用多处理对UDF进行批处理

pandas fill和bfill基于另一列中的条件

从嵌套极轴列的列表中删除元素

用fft计算指数复和代替求和来模拟衍射?

极柱内丢失类型信息""

如果不使用. to_list()[0],我如何从一个pandas DataFrame中获取一个值?

如何在python tkinter中绑定键盘上的另一个回车?