Python的协程操作几种实现方式总结

 更新时间:2025年09月01日 10:39:46   作者:EvanSun__  
在Python中协程(Coroutine)是一种强大的并发编程工具,允许你以非阻塞方式处理I/O密集型任务、异步操作等场景,这篇文章主要介绍了Python的协程操作几种实现方式,需要的朋友可以参考下

并发的本质: 切换+保存状态

概念

协程是单线程下的并发,又称微线程,纤程。英文名Coroutine。是python个中另外一种实现多任务的方式,只不过比线程更小占用更小执行单元(理解为需要的资源)。为啥说它是一个执行单元,因为它自带CPU上下文。这样只要在合适的时机,我们可以把一个协程切换到另一个协程。只要这个过程中保存或恢复 CPU上下文那么程序还是可以运行的。

协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

通俗理解

在一个线程中的某个函数,可以在任何地方保存当前函数的一些临时变量等信息,然后切换到另外一个函数中执行,注意不是通过调用函数的方式做到的,并且切换的次数以及什么时候再切换到原来的函数都由开发者自己确定

协程和线程差异

在实现多任务时, 线程切换从系统层面远不止保存和恢复CPU上下文这么简单。 操作系统为了程序运行的高效性每个线程都有自己缓存Cache等等数据,操作系统还会帮你做这些数据的恢复操作。 所以线程的切换非常耗性能。但是协程的切换只是单纯的操作CPU的上下文,所以一秒钟切换个上百万次系统都抗的住。

协程的优势

  1. 协程的切换开销更小,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级

  2. 单线程内就可以实现并发的效果,最大限度地利用cpu

协程的缺点

  1. 协程的本质是单线程下,无法利用多核,可以是一个程序开启多个进程,每个进程内开启多个线程,每个线程内开启协程
  2. 协程指的是单个线程,因而一旦协程出现阻塞,将会阻塞整个线程

协程的特点

  1. 必须在只有一个单线程里实现并发
  2. 修改共享数据不需加锁
  3. 用户程序里自己保存多个控制流的上下文栈
  4. 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了 gevent模块(select机制))

协程的本质

协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率。

实现方式

方式一 Greenlet 模块

首先要安装Greenlet模块 pip install greenlet

import time
from greenlet import greenlet

def study(name):
    print(f"{name} 正在学习面向对象...")
    time.sleep(2)
    g2.switch("qiaoba")		
    print(f"{name} 正在学习协程...")
    g2.switch("qiaoba")

def play(name):
    print(f"{name} 正在玩超级玛丽...")
    time.sleep(2)
    g1.switch("lufei")
    print(f"{name} 正在玩CS...")

g1 = greenlet(study)	# xxx = greenlet(fun方法名)
g2 = greenlet(play)
g1.switch("lufei")		# 使用xxx.switch("参数") 手动切换另一个方法

缺点是遇到IO阻塞不会自动切换,需要人工切换,比较麻烦

方式二 Gevent模块

python还有一个比greenlet更强大的并且能够自动切换任务的模块 gevent

原理是当一个greenlet遇到IO(指的是input output 输入输出,比如网络、文件操作等)操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO

首先要安装Gevent模块 pip3 install gevent

用法
g1 = gevent.spawn(fun方法名,args参数1,x=args参数2...)  # 创建一个协程对象g1,参数是传给方法的位置实参或关键字实参
g1.join()  # 等待g1结束
gevent.joinall([g1,g2])  # 等待g1g2全部结束
g1.value   # 获得g1的返回值

原来的gevent不能识别原程序中的耗时操作的代码,必须要替换为gevent自带的方法。

可以通过 打补丁 的方式将程序中用到的耗时操作的代码,自动替换为gevent中自己实现的模块,这样例如time.sleep()等耗时操作gevent便可以识别了

from gevent import monkey
monkey.patch_all()  # 自动替换全部耗时操作的代码

方式三(模拟) yield

import time

def consumer():
    while 1:
        x = yield 0
        print(x)
        
def producer():
    g = consumer()
    g.__next__()
    for i in range(100):
        g.send(i)
        print(i)
start = time.time()
producer()
print(time.time()-start)  # 0.0010056495666503906

start = time.time()
for i in range(100):
    print(i)
print(time.time()-start)  # 0.0010077953338623047

方法四 asyncio模块(已过时)

在Python3.4之前官方未提供协程的类库,一般大家都是使用greenlet等其 他来实现。在Python3.4发布后官方正式支持协程,即:asyncio模块。

import asyncio
@asyncio.coroutine
def func1():
    print(1)
    yield from asyncio.sleep(2)  # 遇到IO耗时操作,自动化切换到tasks中的其他任务
print(2)
@asyncio.coroutine
def func2():
    print(3)
    yield from asyncio.sleep(2) # 遇到IO耗时操作,自动化切换到tasks中的其他任务
    print(4)
tasks = [
    asyncio.ensure_future( func1() ),
    asyncio.ensure_future( func2() )
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

方法五 async & awit 模块

async & awit 关键字在Python3.5版本中正式引入,基于他编写的协程代码 其实就是 上一示例 的加强版,让代码可以更加简便。Python3.8之后@asyncio.coroutine 装饰器就会被移除,推荐使用 async & awit 关键字实现协程代码。

import asyncio
async def func1():
   print(1)
   await asyncio.sleep(2)
   print(2)
async def func2():
   print(3)
   await asyncio.sleep(2)
   print(4)
tasks = [
   asyncio.ensure_future(func1()),
   asyncio.ensure_future(func2())
]
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

进程、线程和协程的简单总结

  • 进程是资源分配的单位
  • 线程是操作系统调度的单位
  • 进程切换需要的资源很最大,效率很低
  • 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下)
  • 协程切换任务资源很小,效率高
  • 多进程、多线程根据cpu核数不一样可能是并行的,但是协程是在一个线程中 所以是并发

总结 

到此这篇关于Python的协程操作几种实现方式的文章就介绍到这了,更多相关Python协程操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Python自动化神器Playwright的用法详解

    Python自动化神器Playwright的用法详解

    python Playwright 是一个 Python 库,它提供了一个高级 API,用于自动化 Web 浏览器,它支持 chrome、firefox 和 webkit 浏览器,并提供了一种简单易用的方法来模拟用户在浏览器中的行为,本文小编将详细的给大家介绍一下Python自动化神器Playwright的用法
    2025-04-04
  • python输出100以内的质数与合数实例代码

    python输出100以内的质数与合数实例代码

    本文通过实例代码给大家介绍了python输出100以内的质数与合数的方法,非常不错,具有一定的参考借鉴价值,需要的朋友参考下吧
    2018-07-07
  • 从入门到精通详解Python虚拟环境完全指南

    从入门到精通详解Python虚拟环境完全指南

    Python虚拟环境是一个独立的Python运行环境,它允许你为不同的项目创建隔离的Python环境,下面小编就来和大家详细介绍一下吧
    2025-08-08
  • 在Mac下使用python实现简单的目录树展示方法

    在Mac下使用python实现简单的目录树展示方法

    今天小编就为大家分享一篇在Mac下使用python实现简单的目录树展示方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-11-11
  • TensorFlow的环境配置与安装方法

    TensorFlow的环境配置与安装方法

    这篇文章主要介绍了TensorFlow的环境配置与安装方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-02-02
  • Python中提取人脸特征的三种方法详解

    Python中提取人脸特征的三种方法详解

    这篇文章主要和大家分享三个Python中提取人脸特征的方法,文中的示例代码讲解详细,对我们学习Python有一定的帮助,需要的可以参考一下
    2022-05-05
  • python通过Windows下远程控制Linux系统

    python通过Windows下远程控制Linux系统

    这篇文章主要为大家详细介绍了python通过Windows下远程控制Linux系统,实现对socket模块认识,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • python代码实现扫码关注公众号登录的实战

    python代码实现扫码关注公众号登录的实战

    本文主要介绍了python代码实现扫码关注公众号登录的实战,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • Python+Opencv实现图像模板匹配详解

    Python+Opencv实现图像模板匹配详解

    模板匹配可以看作是对象检测的一种非常基本的形式。使用模板匹配,我们可以使用包含要检测对象的“模板”来检测输入图像中的对象。本文为大家介绍了图像模板匹配的实现方法,需要的可以参考一下
    2022-09-09
  • Python全栈之学习JQuery

    Python全栈之学习JQuery

    这篇文章主要为大家介绍了Python全栈之JQuery,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01

最新评论