首页 > 其他分享 >31. 协程的使用

31. 协程的使用

时间:2024-11-12 17:31:05浏览次数:1  
标签:协程 31 使用 任务 gevent sleep time print

一、什么是协程

  从 Python 3.4 开始,Python 加入了协程的概念,使用 asyncio 模块实现协程。但这个版本的协程还是以生成器对象为基础。 Python 3.5 中增加了 async、await 关键字,使协程的实现更加方便。

  协程(Coroutine),又称 微线程,是一种运行运行在用户态的轻量级线程。协程可以在单线程的情况下实现并发。

  协程拥有自己的寄存器上下文和栈。协程在调度切换时,将寄存器下上文和栈保存到其它地方,等切换回来的时候,再恢复先前保存的寄存器上下文和栈。因此,协程能保留上一次调用时的状态,即所有局部状态的一个特定组合,每次过程重入,就相当于进入上一次调用的状态。

  协程本质上是个单进程,相对于多进程来说,它没有线程上下文切换的开销,没有原子操作锁定及同步的开销。

  关于协程,我们还需要了解以下几个概念:

  • event_loop:事件循环,相当于无限循环,我们可以把一些函数注册到这个事件循环上,当满足发生条件的时候,就调用对应的处理方法;
  • coroutine:在 Python 中常指代协程对象类型,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用不会立即被执行,而是会返回一个协程对象;
  • task:任务,这是协程对象的进一步封装,包含协程对象的各个状态;
  • future:代表将来执行或者没有执行的任务的结果,实际上和 task 没有本质区别;

二、协程的使用

import asyncio
import time

# 协程函数,定义函数的时候,使用async关键字修饰的函数
async def work(x):
    print(f"work({x}) start!")
    print(f"当前任务的参数为:{x}")

    # 遇到IO操作挂起当前协程(任务),等待IO操作完成之后再继续往下执行
    # 当前协程挂起时,事件循环可以去执行其它任务
    await asyncio.sleep(x)

    print(f"work({x}) end!")

    return f"当前任务的返回值:{x}"

async def main():
    print("开始执行main()函数内部代码!")

    tasks = [
        # asyncio.create_task()创建一个task对象
        asyncio.create_task(work(2)),
        # asyncio.ensure_future()创建一个task对象
        asyncio.ensure_future(work(3)),
        asyncio.create_task(work(5)),
    ]

    # asyncio.wait()接收的参数为task对象,返回一个二值元组
    # done接收任务的返回值,pending接收任务的状态
    done, pending = await asyncio.wait(tasks)
    for item in done:
        print(item)

    print("main()函数内部代码执行完毕!")

if __name__ == "__main__":
    start  = time.time()

    # 协程对象,执行协程函数()得到的协程对象
    # 执行协程函数创建协程对象,函数内部的代码不会执行
    result = main()

    # 去生成或获取一个事件循环
    # loop = asyncio.get_event_loop()
    # 将任务放到任务列表中
    # loop.run_until_complete(result)

    # Python 3.7之后的版本可以简化为如下代码
    asyncio.run(result)

    print(time.time() - start)

  协程函数 就是使用 async 关键字修饰的函数。协程对象 就是执行协程函数得到的对象。执行协程函数时,协程函数内部的代码不会执行。如果想要运行协程函数内部代码,必须要将协程对象交给事件循环来处理。当我们把协程对象传递给 run_until_complete() 方法时,实际它将 coroutine 封装成 task 对象。

  事件循环 可以理解为一个死循环,它循环检测并执行某些代码,它的实现原理如下:

任务列表 = [任务1, 任务2, 任务3, ...]

while True:
    可执行的任务列表, 已完成的列表列表 = 去任务列表中检查所有的任务,将“可执行”和“已完成”的任务返回
    for 就绪任务 in 可执行的任务列表:
        执行已就绪的任务

    for 已完成的任务 in 已完成的任务列表:
        在任务列表中移除“已完成”的任务

    if 任务列表中的任务都已完成:
        break

  要实现异步处理,先要有挂起操作,当一个任务需要等待 IO 结果的时候,可以挂起当前任务,转而执行其它任务,这样才能充分利用好资源。

  await 关键字可以将耗时等待的操作挂起,让出控制权。如果协程在执行的时候遇到 await,事件循环就会将本协程挂起,转而执行别的协程,直到其它协程挂起或执行完毕。

  await 后面的对象必须是如下格式之一:

  • 一个原生的协程对象;
  • 一个由 types.coroutine 修饰的生成器,这个生成器可以返回协程对象;
  • 有一个包含 __await__ 方法的对象返回的一个迭代器;

三、手动实现协程

  进程、线程创建完之后,到底是哪个进程、线程执行去执行,这是不确定的,这需要有操作系统来进行计算(调度算法,例如优先级调度)。而协程是可以人为来控制的。程序员在代码层面上检测所有的 IO 操作,一旦遇到 IO 操作,程序员在代码级别完成切换,这样给 CPU 的感觉就是这个程序一直运行,没有 IO 操作,从而提升程序的运行效率。

3.1、使用greenlet模块实现协程

  我们可以使用 greenlet 模块实现协程,但遇到 IO(指的是 input output,输入输出,比如,网络、文件操作等) 操作时,还需要人工切换。

  我们可以在终端中使用 pip 命令安装 greelet 模块:

pip install greenlet
import time

from greenlet import greenlet

def task1():
    while True:
        print("task1 work!")
        g2.switch()
        time.sleep(0.5)

def task2():
    while True:
        print("task2 work!")
        g1.switch()
        time.sleep(0.5)

if __name__ == "__main__":
    g1 = greenlet(task1)
    g2 = greenlet(task2)

    g1.switch()

3.2、使用gevent模块实现协程

  greenlet 模块已经实现了协程,但还需要人工切换。Python 还提供了一个能自动切换任务的 gevent 模块。其原理是当一个 greenlet 遇到 IO 就会自动切换到其它的 greenlet 再执行,而不是等待 IO。

  我们可以在终端中使用 pip 命令安装 gevent 模块:

pip install gevent
import time

from gevent import spawn

# gevent模块本身无法检测常见的一些IO操作
# 在使用的时候需要额外导入monkey模块
from gevent import monkey
monkey.patch_all()

def say_hello():
    print("hello")
    time.sleep(2)
    print("hello")

def say_hi():
    print("hi")
    time.sleep(3)
    print("hi")

def say_good():
    print("good")
    time.sleep(5)
    print("good")

start_time = time.time()

g1 = spawn(say_hello)
g2 = spawn(say_hi)
g3 = spawn(say_good)

# 等待被检测的任务执行完毕,再往后执行
g1.join()
g2.join()
g3.join()

print(time.time() - start_time)
import gevent
import time

# gevent模块本身无法检测常见的一些IO操作
# 在使用的时候需要额外导入monkey模块
from gevent import monkey
monkey.patch_all()

def say_hello():
    print("hello")
    time.sleep(2)
    print("hello")

def say_hi():
    print("hi")
    time.sleep(3)
    print("hi")

def say_good():
    print("good")
    time.sleep(5)
    print("good")

start_time = time.time()

gevent.joinall([
    gevent.spawn(say_hello),
    gevent.spawn(say_hi),
    gevent.spawn(say_good),
])

print(time.time() - start_time)

time 模块的 sleep() 方法不具备自动切换任务的功能,而 gevent 模块的 sleep() 方法具有该能功能,所以我们使用猴子补丁将 time 模块的 sleep() 方法编程 gevent 模块的 sleep() 方法;

标签:协程,31,使用,任务,gevent,sleep,time,print
From: https://www.cnblogs.com/FlurryHeart/p/18542329

相关文章

  • RestSharp基本使用方法
    关于RestSharpRestSharpisalibrarythatallowsyoutomakeRESTandHTTPcallsin.NETapplications.Itsupportsserialization,parameters,asyncfunctions,andmore.RestSharp是C#的一个WepApi库,支持通用的Web接口处理,支持序列化、参数化、异步回调等。入门......
  • 使用Nginx反向代理解决http和https跨域问题
    使用Nginx作为反向代理来解决HTTP和HTTPS跨域问题,主要涉及到配置Nginx以添加CORS(跨源资源共享)相关的响应头。以下是具体的配置步骤和解释:通过上述配置,Nginx可以作为反向代理服务器,解决HTTP和HTTPS的跨域问题,同时确保通信的安全性和效率。配置CORS响应头:在Nginx的配置文件......
  • 用PNGMaker.io快速生成透明PNG图像——使用者分享体验
    摘要:PNGMaker.io是一个在线的免费PNG制作工具,可以轻松将文字转换成透明背景的PNG图像,适合各类设计需求。在日常设计工作中,我们常常需要透明背景的PNG图像,但要用专业设计软件制作,步骤多且费时。最近我试用了PNGMaker.io,它可以在线、免费地生成带透明背景的PNG图像,操作简单,效果也......
  • 《Java核心技术 卷I》图形用户界面使用字体
    使用字体字体名(fontfacename)指定一种字体,字体名由字体族名(fontfamilyname)和一个可选的后缀(如果"Bold")组成,例如,“Helvetica”和"HelveticaBold"都属于名为“Helvetica”字体族的字体。要想知道某台特定计算机上可用的字体,可以调用GraphicsEnvironment类的getAvaila......
  • 使用python爬取百度热搜
    文章目录前言一、requests是什么?二、使用步骤1.引入库2.获取页面数据3.使用xpath解析页面,获取词条列表信息4.获取指定元素信息,添加到dataframe中5.保存数据到指定的文件或数据库总结前言本文介绍使用request获取百度热搜的简单功能一、requests是什么?Pythonreq......
  • [RuoYi使用]使用RuoYi的代码生成功能
    目录 一、前言二、创建数据表 三、新建目录四、生成代码五、执行生成的SQL文件生成子目录 六、将生成的代码放入项目七、重新运行程序一、前言若依代码生成器主要用于从数据库表生成对应的实体类、Mapper接口、Service层和Controller层代码,以及相应的前端页面代......
  • 在vue3中使用甘特图动态渲染失败
    最终删除了<stylescoped>的scoped后解决。 在Vue.js中使用DHTMLXGantt是可行的,但要注意某些细节,以确保样式和模板正常工作。Vue的组件体系和样式作用域可能会影响Gantt图的样式应用。以下是一些解决方案:1.检查CSS作用域如果您的CSS使用了<stylescoped>,那么这......
  • birdwatcher安装使用
    1.官网下载二进制版本的安装包,解压到相应目录即可运行https://github.com/milvus-io/birdwatcher  2.尝试使用birdwatcher链接注意单机部署的milvus默认是没有暴露etcd端口的,需要修改配置重新部署[root@localhostbirdwatcher]#./birdwatcherOffline>connect--etcd1......
  • swiper vue-awesome-swiper基本使用以及注意事项
    本文中使用vue来演示1.安装swiper下载插件(最新版本的swiper可能会出现未知bug,所以这里使用5.4.5)[email protected]@4.1.0-seve注意!!安装swiper和vue-awesome-swiper一定要对应上版本,否则引入,或者语法会出报错,每一个swiper版本都有对应的vue-awe......
  • 将文字转换为运动:使用AMD GPU生成视频指南
    TransformingWordsintoMotion:AGuidetoVideoGenerationwithAMDGPU—ROCmBlogs发布日期:2024年4月24日作者: DouglasJia本博客介绍了通过增强稳定扩散模型在文本到视频生成方面的进展,并展示了使用阿里巴巴的ModelScopeT2V模型在AMDGPU上生成视频的过程。......