Python 协程的本质?原来也不过如此

协程是什么?有什么用?一文教你完全掌握协程的本质。 首先,本文不是带你浏览源代码,然后对照原始代码给你讲 Python 标准的实现。相反,我们会从实际问题出发,思考解决问题的方案,一步步体会解决方案的演进路径,最重要的,希望能在过程中获得知识系统性提升。:warning: 本文仅是提供了一个独立的思考方向,并未遵循历史和现有实际具体的实现细节。其次,阅读这篇文章需要你对 Python 比较熟悉,至少了解 Python 中的生成器 generator 的概念。 多路复用这是性能的关键。但我们这里只解释概念,其实现细节不是重点,这对我们理解 Python 的协程 阅读详情

前言

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。

PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取

python免费学习资料、代码以及交流解答点击即可加入


这两天因为一点个人原因写了点好久没碰的 Python ,其中涉及到「协程」编程,上次搞的时候,它还是 Web 框架 tornado 特有的 feature,现在已经有 async、await 关键字支持了。思考了一下其实现,回顾了下这些年的演变,觉得还有点意思。

都是单线程,为什么原来低效率的代码用了 async、await 加一些异步库就变得效率高了?

如果在做基于 Python 的网络或者 Web 开发时,对于这个问题曾感到疑惑,这篇文章尝试给一个答案。

0x00 开始之前

首先,本文不是带你浏览源代码,然后对照原始代码给你讲 Python 标准的实现。相反,我们会从实际问题出发,思考解决问题的方案,一步步体会解决方案的演进路径,最重要的,希望能在过程中获得知识系统性提升。
⚠️ 本文仅是提供了一个独立的思考方向,并未遵循历史和现有实际具体的实现细节。

其次,阅读这篇文章需要你对 Python 比较熟悉,至少了解 Python 中的生成器 generator 的概念。

0x01 IO 多路复用

这是性能的关键。但我们这里只解释概念,其实现细节不是重点,这对我们理解 Python 的协程已经足够了,如已足够了解,前进到 0x02。
首先,你要知道所有的网络服务程序都是一个巨大的死循环,你的业务逻辑都在这个循环的某个时刻被调用:

def handler(request):
    # 处理请求
    pass

# 你的 handler 运行在 while 循环中
while True:
    # 获取一个新请求
    request = accept()
    # 根据路由映射获取到用户写的业务逻辑函数
    handler = get_handler(request)
    # 运行用户的handler,处理请求
    handler(request)

设想你的 Web 服务的某个 handler,在接收到请求后需要一个 API 调用才能响应结果。

对于最传统的网络应用,你的 API 请求发出去后在等待响应,此时程序停止运行,甚至新的请求也得在响应结束后才进得来。如果你依赖的 API 请求网络丢包严重,响应特别慢呢?那应用的吞吐量将非常低。

很多传统 Web 服务器使用多线程技术解决这个问题:把 handler 的运行放到其他线程上,每个线程处理一个请求,本线程阻塞不影响新请求进入。这能一定程度上解决问题,但对于并发比较大的系统,过多线程调度会带来很大的性能开销。

IO 多路复用可以做到不使用线程解决问题,它是由操作系统内核提供的功能,可以说专门为这类场景而生。简单来讲,你的程序遇到网络IO时,告诉操作系统帮你盯着,同时操作系统提供给你一个方法,让你可以随时获取到有哪些 IO 操作已经完成。就像这样:

# 操作系统的IO复用示例伪代码
# 向操作系统IO注册自己关注的IO操作的id和类型
io_register(io_id, io_type)
io_register(io_id, io_type)

# 获取完成的IO操作
events = io_get_finished()

for (io_id, io_type) in events:
    if io_type == READ:
        data = read_data(io_id) 
    elif io_type == WRITE:
        write_data(io_id,data)

把 IO 复用逻辑融合到我们的服务器中,大概会像这样:

call_backs = {}

def handler(req):
    # do jobs here
    io_register(io_id, io_type)
    def call_back(result):
        # 使用返回的result完成剩余工作...
    call_backs[io_id] = call_back

# 新的循环
while True:
    # 获取已经完成的io事件
    events = io_get_finished()
    for (io_id, io_type) in events:
        if io_type == READ: # 读取
            data = read(io_id) 
            call_back = call_backs[io_id]
            call_back(data)
        else:
            # 其他类型io事件的处理
            pass

    # 获取一个新请求
    request = accept()
    # 根据路由映射获取到用户写的业务逻辑函数
    handler = get_handler(request)
    # 运行用户的handler,处理请求
    handler(request)

我们的 handler 对于 IO 操作,注册了回调就立刻返回,同时每次迭代都会对已完成的 IO 执行回调,网络请求不再阻塞整个服务器。

上面的伪代码仅便于理解,具体实现细节更复杂。而且就连接受新请求也是在从操作系统得到监听端口的 IO 事件后进行的。

我们如果把循环部分还有 call_backs 字典拆分到单独模块,就能得到一个 EventLoop,也就是 Python 标准库 asyncio 包中提供的 ioloop。

0x02 用生成器消除 callback

着重看下我们业务中经常写的 handler 函数,在有独立的 ioloop 后,它现在变成类似这样:

def handler(request):
    # 业务逻辑代码...
    # 需要执行一次 API 请求
    def call_back(result):
        # 使用 API 返回的result完成剩余工作
        print(result)
    # 没有io_call这个方法,这里只是示意,表示注册一个IO操作
    asyncio.get_event_loop().io_call(api, call_back)

到这里,性能问题已经解决了:我们不再需要多线程就能源源不断接受新请求,而且不用care依赖的 API 响应有多慢。

但是我们也引入了一个新问题,原来流畅的业务逻辑代码现在被拆成了两部分,请求 API 之前的代码还正常,请求 API 之后的代码只能写在回调函数里面了。

这里我们业务逻辑只有一个 API 调用,如果有多个 API ,再加上对 Redis 或者 MySQL 的调用(它们本质也是网络请求),整个逻辑会被拆分的更散,这对业务开发是一笔负担。

对于有匿名函数的一些语言(没错就是JavaScript),还可能会引发所谓的「回调地狱」。

接下来我们想办法解决这个问题。
我们很容易会想到:如果函数在运行到网络 IO 操作处后能够暂停,完成后又能在断点处唤醒就好了。
如果你对 Python 的「生成器」熟悉,你应该会发现,它恰好具有这个功能:

def example():
    value = yield 2
    print("get", value)
    return value

g = example()
# 启动生成器,我们会得到 2
got = g.send(None)
print(got)  # 2

try:
    # 再次启动 会显示 "get 4", 就是我们传入的值
    got = g.send(got*2)
except StopIteration as e:
    # 生成器运行完成,将会print(4),e.value 是生成器return的值
    print(e.value)

函数中有 yield 关键字,调用函数将会得到一个生成器,生成器一个关键的方法 send() 可以跟生成器交互。

g.send(None) 会运行生成器内代码直到遇到 yield,并返回其后的对象,也就是 2,生成器代码就停在这里了,直到我们再次执行 g.send(got2),会把 22 也就是 4 赋值给yield 前面的变量 value,然后继续运行生成器代码。

yield 在这里就像一扇门,可以把一件东西从这里送出去,也可以把另一件东西拿进来。

如果 send 让生成器运行到下一个 yield 前就结束了,send 调用会引发一个特殊的异常StopIteration,这个异常自带一个属性 value,为生成器 return 的值。

如果我们把我们的 handler 用 yield 关键字转换成一个生成器,运行它来把 IO 操作的具体内容返回,IO 完成后的回调函数中把 IO 结果放回并恢复生成器运行,那就解决了业务代码不流畅的问题了:

def handler(request):
    # 业务逻辑代码...
    # 需要执行一次 API 请求,直接把 IO 请求信息yield出去
    result = yield io_info
    # 使用 API 返回的result完成剩余工作
    print(result)

# 这个函数注册到ioloop中,用来当有新请求的时候回调
def on_request(request):
    # 根据路由映射获取到用户写的业务逻辑函数
    handler = get_handler(request)
    g = handler(request)
    # 首次启动获得io_info
    io_info = g.send(None)

    # io完成回调函数
    def call_back(result):
        # 重新启动生成器
        g.send(result)

    asyncio.get_event_loop().io_call(io_info, call_back)

上面的例子,用户写的 handler 代码已经不会被打散到 callback 中,on_request 函数使用 callback 和 ioloop 交互,但它会被实现在 Web 框架中,对用户不可见。

上面代码足以给我们提供用生成器消灭的 callback 的启发,但局限性有两点:

1.业务逻辑中仅发起一次网络 IO,但实际中往往更多
2.业务逻辑没有调用其他异步函数(协程),但实际中我们往往会调用其他协程

0x03 解决完整调用链

我们来看一个更复杂的例子:
其中 request 执行真正的 IO,func1、func2 仅调用。显然我们的代码只能写成这样:

def func1():
    ret = yield request("http://test.com/foo")
    ret = yield func2(ret)
    return ret

def func2(data):
    result = yield request("http://test.com/"+data)
    return result

def request(url):
    # 这里模拟返回一个io操作,包含io操作的所有信息,这里用字符串简化代替
    result = yield "iojob of %s" % url
    return result

对于 request,我们把 IO 操作通过 yield 暴露给框架。

对于 func1 和 func2,调用 request 显然也要加 yield 关键字,否则 request 调用返回一个生成器后不会暂停,继续执行后续逻辑显然会出错。

这基本就是我们在没有 yield from、aysnc、await 时代,在 tornado 框架中写异步代码的样子。
要运行整个调用栈,大概流程如下:
1.调用 func1() 得到生成器
2.调用 send(None) 启动它得到会得到 request("http://test.com/foo") 的结果,还是生成器对象
3.send(None) 启动由 request() 产生的生成器,会得到 IO 操作,由框架注册到 ioloop 并指定回调
4.IO 完成后的回调函数内唤醒 request 生成器,生成器会走到 return 语句结束
5.捕获异常得到 request 生成器的返回值,将上一层 func1 唤醒,同时又得到 func2() 生成器
6.继续执行...

对算法和数据结构熟悉的朋友遇到这种前进后退的遍历逻辑,可以递归也可以用栈,因为递归使用生成器还做不到,我们可以使用栈,其实这就是「调用栈」一词的由来。

借助栈,我们可以把整个调用链上串联的所有生成器对表现为一个生成器,对其不断 send 就能不断得到所有 IO 操作信息并推动调用链前进,实现方法如下:
1.第一个生成器入栈
2.调用 send,如果得到生成器就入栈并进入下一轮迭代
3.遇到到 IO 请求 yield 出来,让框架注册到 ioloop
4.IO 操作完成后被唤醒,缓存结果并出栈,进入下一轮迭代,目的让上层函数使用 IO 结果恢复运行
5.如果一个生成器运行完毕,也需要和4一样让上层函数恢复运行

如果实现出来,代码不长但信息量比较大。

它把整个调用链对外变成一个生成器,对其调用 send,就能整个调用链中的 IO,完成这些 IO,继续推动调用链内的逻辑执行,直到整体逻辑结束:

def wrapper(gen):
    # 第一层调用 入栈
    stack = Stack()
    stack.push(gen)

    # 开始逐层调用
    while True:
        # 获取栈顶元素
        item = stack.peak()

        result = None
        # 生成器
        if isgenerator(item):
            try:
                # 尝试获取下层调用并入栈
                child = item.send(result)
                stack.push(child)
                # result 使用过后就还原为None
                result = None
                # 入栈后直接进入下次循环,继续向下探索
                continue
            except StopIteration as e:
                # 如果自己运行结束了,就暂存result,下一步让自己出栈
                result = e.value
        else:  # IO 操作
            # 遇到了 IO 操作,yield 出去,IO 完成后会被用 IO 结果唤醒并暂存到 result
            result = yield item

        # 走到这里则本层已经执行完毕,出栈,下次迭代将是调用链上一层
        stack.pop()
        # 没有上一层的话,那整个调用链都执行完成了,return        
        if stack.empty():
            print("finished")
            return result

这可能是最复杂的部分,如果看起来吃力的话,其实只要明白,对于上面示例中的调用链,它可以实现的效果如下就好了:

w = wrapper(func1())
# 将会得到 "iojob of http://test.com/foo"
w.send(None)
# 上个iojob foo 完成后的结果"bar"传入,继续运行,得到 "iojob of http://test.com/bar"
w.send("bar")
# 上个iojob bar 完成后的结构"barz"传入,继续运行,结束。
w.send("barz")

有了这部分以后框架再加上配套的代码:

# 维护一个就绪列表,存放所有完成的IO事件,格式为(wrapper,result) 
ready = []

def on_request(request):
    handler = get_handler(request)
    # 使用 wrapper 包装后,可以只通过 send 处理 IO 了
    g = wrapper(func1())
    # 把开始状态直接视为结果为None的就绪状态
    ready.append((g, None))

# 让ioloop每轮循环都执行此函数,用来处理的就绪的IO
def process_ready(self):
    def call_back(g, result):
        ready.append((g, result)) 

    # 遍历所有已经就绪生成器,将其向下推进
    for g, result in self.ready:  
        # 用result唤醒生成器,并得到下一个io操作
        io_job = g.send(result)
        # 注册io操作 完成后把生成器加入就绪列表,等待下一轮处理
        asyncio.get_event_loop().io_call(
            io_job, lambda result: ready.append((g, result)

这里核心思想是维护一个就绪列表,ioloop 每轮迭代都来扫一遍,推动就绪的状态的生成器向下运行,并把新的 IO 操作注册,IO 完成后再次加入就绪,经过几轮 ioloop 的迭代一个 handler 最终会被执行完成。

至此,我们使用生成器写法写业务逻辑已经可以正常运行。

0x04 提高扩展性

如果到这里能读懂, Python 的协程原理基本就明白了。

我们已经实现了一个微型的协程框架,标准库的实现细节跟这里看起来大不一样,但具体的思想是一致的。

我们的协程框架有一个限制,我们只能把 IO 操作异步化,虽然在网络编程和 Web 编程的世界里,阻塞的基本只有 IO 操作,但也有一些例外,比如我想让当前操作 sleep 几秒,用 time.sleep() 又会让整个线程阻塞住,就需要特殊实现。再比如,可以把一些 CPU 密集的操作通过多线程异步化,让另一个线程通知事件已经完成后再执行后续。

所以,协程最好能与网络解耦开,让等待网络IO只是其中一种场景,提高扩展性。

Python 官方的解决方案是让用户自己处理阻塞代码,至于是向 ioloop 来注册 IO 事件还是开一个线程完全由你自己,并提供了一个标准「占位符」Future,表示他的结果等到未来才会有,其部分原型如下:

class Future:
    # 设置结果
    def set_result(result): pass
    # 获取结果
    def result():  pass
    #  表示这个future对象是不是已经被设置过结果了
    def done(): pass
    # 设置在他被设置结果时应该执行的回调函数,可以设置多个
    def add_done_callback(callback):  pass

我们的稍加改动就能支持 Future,让扩展性变得更强。对于用户代码的中的网络请求函数 request:

# 现在 request 函数,不是生成器,它返回future
def request(url):
    # future 理解为占位符
    fut = Future()

    def callback(result):
        # 当网络IO完成回调的时候给占位符赋值
        fut.set_result(result)
    asyncio.get_event_loop().io_call(url, callback)

    # 返回占位符
    return future

现在,request 不再是一个生成器,而是直接返回 future。
而对于位于框架中处理就绪列表的函数:

def process_ready(self):
    def callback(fut):
        # future被设置结果会被放入就绪列表
        ready.append((g, fut.result()))

    # 遍历所有已经就绪生成器,将其向下推进
    for g, result in self.ready:  
        # 用result唤醒生成器,得到的不再是io操作,而是future
        fut = g.send(result)
        # future被设置结果的时候会调用callback
        fut.add_done_callback(callback)

0x05 发展和变革

许多年前用 tornado 的时候,大概只有一个 yield 关键字可用,协程要想实现,就是这么个思路,甚至 yield 关键字和 return 关键字不能一个函数里面出现,你要想在生成器运行完后返回一个值,需要手动 raise 一个异常,虽然效果跟现在 return 一样,但写起来还是很别扭,不优雅。

后来有了 yield from 表达式。它可以做什么?

通俗地说,它就是做了上面那个生成器 wrapper 所做的事:通过栈实现调用链遍历的 ,它是 wrapper 逻辑的语法糖。

有了它,同一个例子你可以这么写:

def func1():
    # 注意 yield from
    ret = yield from request("http://test.com/foo")
    # 注意 yield from
    ret = yield from func2(ret) 
    return ret

def func2(data):
    # 注意 yield from
    result = yield from request("http://test.com/"+data)
    return result

# 现在 request 函数,不是生成器,它返回future
def request(url):
    # 同上基于future实现的request

然后你就不再需要那个烧脑的 wrapper 函数了:

g = func1()
# 返回第一个请求的 future 
g.send(None)
# 继续运行,自动进入func2 并得到第它里面的那个future
g.send("bar")
# 继续运行,完成调用链剩余逻辑,抛出StopIteration异常
g.send("barz")

yield from 直接打通了整个调用链,已经是很大的进步了,但是用来异步编程看着还是别扭,其他语言都有专门的协程 async、await 关键字了,直到再后来的版本把这些内容用专用的 async、await 关键字包装,才成为今天比较优雅的样子。

0x06 总结和比较

总的来说, Python 的原生的协程从两方面实现:
1.基于 IO 多路复用技术,让整个应用在 IO 上非阻塞,实现高效率
2.通过生成器让分散的 callback 代码变成同步代码,减少业务编写困难

有生成器这种对象的语言,其 IO 协程实现大抵如此,JavaScript 协程的演进基本一模一样,关键字相同,Future 类比 Promise 本质相同。

但是对于以协程出名的 Go 的协程实现跟这个就不同了,并不显式地基于生成器。

如果类比的话,可以 Python 的 gevent 算作一类,都是自己实现 runtime,并 patch 掉系统调用接入自己的 runtime,自己来调度协程,gevent 专注于网络相关,基于网络 IO 调度,比较简单,而 Go 实现了完善的多核支持,调度更加复杂和完善,而且创造了基于 channel 新编程范式

Mac用户必看:Parallels Desktop 18.1.1最新破解版安装与优化全攻略(附资源) 本文为Mac用户提供Parallels Desktop 18.1.1的详细安装与优化指南,涵盖虚拟化技术选型、专业级配置流程及性能调优技巧。重点解析Parallels Desktop在图形加速、系统资源调度方面的核心优势,并分享生产力增强技巧与典型应用场景实战方案,帮助用户最大化利用这一高效虚拟化工具。 阅读详情

相关推荐

排查单片机不工作的‘玄学’故障:从LDO选型、复位电路到焊接不良的完整避坑指南

本文深入剖析单片机不工作的常见硬件故障,从LDO选型、复位电路设计到PCB布局和焊接工艺,提供系统性排查方法。重点分析3.3V电源稳定性、LDO动态响应特性及复位电路设计要点,帮助工程师快速定位和解决嵌入式系统中的'玄学'故障,提升产品可靠性。

weixin_30655569的博客 389

Python Tornado框架(ioloop对象分析)

转自:https://www.cnblogs.com/jasonwang-2016/p/5950548.html 网上都说nginx和lighthttpd是高性能web服务器,而tornado也是著名的高抗负载应用,它们间有什么相似处呢?上节提到的ioloop对象是如何循环的呢?往下看。 首先关于TCP服务器的开发上节已经提过,很明显那个三段式的示例是个效率很低的(因为只有一个连接被端开新连接...

走在搬砖的路上! 1242

VLA-JEPA:具身智能中潜空间世界模型的工程落地实践

世界模型是具身智能实现环境理解与因果推演的核心基础,其本质是在状态空间中建模‘动作→结果’的动力学规律。传统基于像素或文本的世界模型因高维、非平稳、弱语义等缺陷,在真实机器人场景中鲁棒性差、部署成本高。JEPA(联合嵌入预测架构)通过构建物理约束的潜空间,将视觉-语言-动作(VLA)信号统一编码为低维、可微、可解释的状态表征,显著提升模型对抖动、遮挡、光照突变等干扰的适应能力,并支持实时闭环控制。该范式已在UR5e、Franka等多构型机械臂及Jetson边缘平台完成实测验证,成为VLA系统从实验室走向产线

weixin_34357928的博客 336

python的本质和语法(全面解析)

python的本质

2301_78960337的博客 1155

AI 大模型在电商精准营销中的应用

例如,对于用户 A 和用户 B,通过比较他们对共同评价过的商品的评分差异来确定相似度。其中,(Gain(D,a))表示特征(a)对数据集(D)的信息增益,(Ent(D))表示数据集(D)的信息熵,(V)表示特征(a)的取值个数,(D^{v})表示特征(a)取值为(v)的样本子集。其中,(sim(A,B))表示用户 A 和用户 B 的相似度,(I)表示用户 A 和用户 B 共同评价过的商品集合,(R_{A,i})和(R_{B,i})分别表示用户 A 和用户 B 对商品(i)的评分。(三)营销活动效果预测。

alittlehippo的博客 1627

反卷积通俗详细解析与nn.ConvTranspose2d重要参数解释

反卷积的作用 卷积中padding的几个概念 No Padding Half(Same) Padding Full Padding 反卷积 反卷积中的Padding参数 反卷积的stride参数 反卷积总结 参考资料

iioSnail的博客 2万+

python是什么?python能干什么?一篇文章让你了解python的本质

能用到Python 的地方非常多。无论是从入门级小白到专业级的大佬,数据挖掘、科学计算、图像处理、人工智能,Python 都可以胜任。或许是因为这种万能属性,现在有很多的小伙伴都开始学习 Python。而现在 Python 的火爆已经来到了程序员的圈子外,进入了国务院《新一代人工智能发展规划的通知》里。Python 也已经走进了小学生的课程里,其实不是小学生,为了你自己的发展前景,或许我才是最该学 Python 的人。

wly55690的博客 919

如何对TBSS的结果进行统计学的分析,已完成置换检验,如何在最终的TBSS输出结果提取除具体值及如何做进一步相关性分析??

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值。

**My Coding Family** 1442

python 协程_理解Python协程的本质

这两天因为一点个人原因写了点好久没碰的python,其中涉及到协程编程,上次搞的时候,它还是web框架tornado特有的feature,现在已经有async await 关键字支持了。思考了一下其实现,回顾了下这些年的演变,觉得还有点意思。 都是单线程,为什么原来低效率的代码用了async await加一些异步库就变得效率高了?如果你做基于python的网络或者web开发时,对于这个问题曾感到疑...

weixin_39914938的博客 267

杰理SDK入门教程(一):SDK的四种编译方式的使用

杰理SDK提供了四种主要编译方式:1) JL Studio官方IDE,适合初学者;2) VS Code轻量级编辑器,需在SDK根目录打开;3) 命令行/脚本编译,灵活支持自动化;4) Code::Blocks开源IDE。每种方式都有详细操作指南,开发者可根据需求选择,初学者推荐从JL Studio开始熟悉编译流程。环境配置注意事项和编译成功标识均有明确说明,确保开发效率。

qq_42014563的博客 1153

地平线J6的基础简介

地平线J6M芯片:面向中阶辅助驾驶市场的高性价比芯片

weixin_43084908的博客 4468

Python 协程的本质?原来也不过如此,一文详细了解

都是单线程,为什么原来低效率的代码用了async、await加一些异步库就变得效率高了? 如果在做基于 Python 的网络或者 Web 开发时,对于这个问题曾感到疑惑,这篇文章尝试给一个答案。

y1282037271的博客 117

锁的艺术:Java并发中的常用锁策略与实践

对java锁相关内容进行整理分析:乐观锁 VS 悲观锁+自旋锁 VS 适应性自旋锁+无锁 VS 偏向锁 VS 轻量级锁 VS 重量级锁+公平锁 VS 非公平锁+可重入锁 VS 非可重入锁+独享锁 VS 共享锁

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 171万+

谈谈Python协程的本质

作者:毛豆花生来源:https://zhuanlan.zhihu.com/p/330549526这两天因为一点个人原因写了点好久没碰的 Python ,其中涉及到「协程」编程,上次搞的时...

tongtongjing1765的博客 1929

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Python—协程(Coroutine)

协程概念和代码使用

蓝魔的博客 6282

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

【硬核】理解Python协程的本质

本文转载自我的一位好友写的文章。本文章信息量较大,从 IO 多路复用,到生成器的使用,再到 async、await 背后的实现原理,深入浅出,剖析得非常透彻,非常硬核!作者:毛豆花生原文...

Python编程与实战的博客 372

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

python协程

可执行的任务列表, 执行完成的任务列表 = 去任务列表中检查所有的任务, 将“可执行”和“已完成”的任务返回。回调一直是很多异步编程的噩梦,程序员更喜欢用同步的编写方式写异步代码,以避免回调的问题。await`的作用就是等待后面的函数返回结果,只有后面的函数返回结果了,程序才会继续往下执行。这种协程式的来实现异步,要么统一用进程池或线程池来实现异步。一般情况下,不会交叉使用。可以针对耗时的操作进行挂起,就像生成器中的。之后引入的标准库,内置对异步IO的支持。方法,前面不绑定回调的例子中,可以看到。

m0_60861079的博客 1052

Python中的协程

摘要:Python协程(Coroutine)是一种轻量级并发机制,通过async/await语法在单线程内实现异步任务调度。其核心原理是遇到I/O操作时主动让出执行权,待操作完成再恢复,显著提升IO密集型任务(如网络请求)的性能。协程基于生成器实现,通过事件循环自动管理任务切换。典型应用包括爬虫、Web服务器等场景,优势在于轻量、高效和代码简洁,但需注意避免阻塞操作并选择兼容的异步库。协程通过协作式多任务机制,在保持单线程的同时实现高并发。

破烂PAN 的博客 1192

python的本质_【硬核】理解Python协程的本质

本文转载自我的一位好友写的文章。本文章信息量较大,从 IO 多路复用,到生成器的使用,再到、背后的实现原理,深入浅出,剖析得非常透彻,非常硬核!作者:毛豆花生这两天因为一点个人原因写了点好久没碰的 Python ,其中涉及到「协程」编程,上次搞的时候,它还是 Web 框架特有的,现在已经有、关键字支持了。思考了一下其实现,回顾了下这些年的演变,觉得还有点意思。都是单线程,为什么原来低效率的代码用了...

weixin_39771260的博客 121

Python中的 协程

协程的定义 协程: 协程,又称微线程,纤程,英文名Coroutine。 协程由于由程序主动控制切换,没有线程切换的开销,所以执行效率极高。对于IO密集型任务非常适用,如果是cpu密集型,推荐多进程+协程的方式。 线程的切换会保存到CPU的栈里,协程拥有自己的寄存器上下文和栈, 协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈 协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态 协程最主要的作用是在单线程

ZPeng_Yan的博客 1万+

python学习 -- Python的协程操作

摘要: 协程是单线程下的并发实现,通过用户态的任务切换和CPU上下文保存实现高效并发。相比线程,协程切换开销更小,但无法利用多核且阻塞会阻塞整个线程。Python中可通过Greenlet(手动切换)、Gevent(自动切换IO)、yield模拟或async/await实现协程。Gevent通过补丁识别原生IO操作,async/await是官方推荐方式。协程适合高并发IO密集型任务,而多进程/线程适合CPU密集型任务。协程本质是单线程内任务切换的并发,资源占用少但需注意阻塞问题。

EvanSun_ 的博客 1230

python3.7 协程实战

文章目录协程实战cpu密集io密集python3.7 协程代码官网两个例子常用的用法单核上的协程多核上的协程 协程实战 只介绍常见用法,底层用法请回顾 yield from,原理请参考论文 cpu密集 对于cpu密集操作,当然只能靠多进程方式 io密集 对于io密集型操作,传统上用多线程方式。 现在,更加高效的的方式来了,协程 ! 协程的本质是函数回调, 不需要os调度,效率极高,且不需要锁...

(-_-) 4429

python 之协程笔记

协程的本质是用户态线程,由程序自行控制切换时机,无需操作系统介入。协程(Coroutine) 是Python实现单线程并发的核心机制,通过async/await语法实现非阻塞I/O操作,特别适合处理网络请求密集型任务(如Web服务器、爬虫等)

wyz0923的博客 1236

python协程(1): 基本介绍及yield实现协程

https://www.liaoxuefeng.com/wiki/1016959663602400/1017968846697824

Python开发分享的博客 2853

Python异步编程-协程

在使用多个爬虫脚本进行数据爬取和调用大语言模型返回结果的场景中,涉及到大量的网络IO操作。协程能够让网络IO操作并发执行,极大地提升程序的运行效率。在智能体相关的开源项目中,我们也可以经常看到协程的身影。

weixin_44907479的博客 1644

Python与协程从Python2—Python3

协程,又称微线程、纤程,英文名Coroutine;用一句话说明什么是线程的话:协程是一种用户态的轻量级线程。 Python对于协程的支持在python2中还比较简单,但是也有可以使用的第三方库,在python3中开始全面支持,也成为python3的一个核心功能,很值得学习。 协程介绍 协程,又称微线程、纤程,英文名Coroutine;用一句话说明什么是线程的话:协程是一种用户态的轻...

weixin_42556618的博客 1459
上一篇: PyCaret:几行代码轻松搞定从数据处理到模型部署
下一篇: 关于包导入,这三个知识点太多人不知道了
酸菜鱼编程
博客等级 码龄6年 155粉丝 63原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值