Python - 协程基本使用详解【demo】

news2025/1/21 16:32:45

一. 前言

协程(Coroutine)是一种轻量级的线程,也被称为用户级线程或绿色线程。它是一种用户态的上下文切换方式,比内核态的线程切换更为轻量级,能够高效的支持大量并发操作。

2. 使用协程的好处

Python 中的协程是通过 asyncio 模块实现的,使用协程可以带来以下好处:

  1. 轻量级并发操作:协程的运行过程中不需要进行线程切换和上下文切换,避免了这些操作的开销,因此比传统的线程更加轻量级,可以支持更多并发操作。
  2. 提高程序性能:协程避免了线程上下文切换的开销,可以大大提高程序的执行效率。并且在 Python 中,协程的调度是由事件循环(Event
    Loop)来完成的,事件循环采用的是单线程方式,能够充分利用 CPU 资源,进一步提高了程序的性能。
  3. 简化异步编程:协程使得异步编程更加简单,在编写异步程序时可以避免回调地狱(Callback
    Hell)的问题,提高程序的可读性和可维护性。而且通过 Python 的 async/await
    语法,可以使用同步的方式编写异步代码,使得代码更加易于理解和调试。
  4. 更加灵活的控制流程:协程可以方便地进行挂起和恢复操作,因此可以很灵活地控制流程。例如可以在协程中使用条件判断、循环等语句,进行更加复杂的流程控制操作。

总之,协程是一种高效、轻量级的并发编程方式,能够提高程序的性能,简化异步编程,使得控制流程更加灵活,是 Python 中重要的并发编程工具之一。

三. 代码示例

1. 基本使用

import asyncio

async def task1():
    await asyncio.sleep(1)
    print('Task 1 done')

async def task2():
    await asyncio.sleep(2)
    print('Task 2 done')

async def main():
    print('Starting tasks')
    # 并发执行 task1 和 task2 任务
    await asyncio.gather(task1(), task2())
    print('All tasks done')

asyncio.run(main())

运行结果
在这里插入图片描述

2. 进阶使用:异步任务循环任务

方法一:

import asyncio
import random

async def producer(queue):
    while True:
        value = random.randint(0, 10)
        print(f"Produced: {value}")
        await queue.put(value)
        await asyncio.sleep(random.random())

async def consumer(queue):
    while True:
        value = await queue.get()
        print(f"Consumed: {value}")
        await asyncio.sleep(random.random())

async def main():
    queue = asyncio.Queue()
    task_producer = asyncio.create_task(producer(queue))
    task_consumer = asyncio.create_task(consumer(queue))
    await asyncio.gather(task_producer, task_consumer)

asyncio.run(main())

在上面的代码中,我们定义了一个 producer 函数和一个 consumer 函数,它们都接受一个 asyncio.Queue 对象作为输入,并在其中实现协程的逻辑。其中 producer 函数会在队列中不断生成随机数,并将其放入队列中;consumer 函数则会不断从队列中取出随机数并进行消费。

在 main 函数中,我们首先创建了一个 asyncio.Queue 对象用于协程之间的通信,然后使用 asyncio.create_task 函数创建了两个任务,分别是生产者任务和消费者任务。最后,我们使用 asyncio.gather 函数来运行这两个任务,当其中任何一个任务完成时,main 函数也会结束。

需要注意的是,当我们使用协程时,需要使用 await 关键字来挂起协程的执行,等待其他协程的执行或者等待 I/O 操作完成。同时,在协程中不应该使用阻塞式的操作,比如 time.sleep(),而应该使用异步 I/O 操作,比如 asyncio.sleep()

方法二

import asyncio
import random


async def producer(queue):
    while True:
        value = random.randint(0, 10)
        print(f"Produced: {value}")
        await queue.put(value)
        await asyncio.sleep(random.random())

async def consumer(queue):
    while True:
        value = await queue.get()
        print(f"Consumed: {value}")
        await asyncio.sleep(random.random())


async def main():
    queue = asyncio.Queue()
    '''方法一'''
    # task_producer = asyncio.create_task(producer(queue))
    # task_consumer = asyncio.create_task(consumer(queue))
    # await asyncio.gather(task_producer, task_consumer)
    '''方法二'''
    await asyncio.wait([producer(queue), consumer(queue)])


asyncio.run(main())

以上协程的两种启动方式的的区别在于它们等待任务完成的方式不同。

await asyncio.gather(task_producer, task_consumer) 会并发地运行多个任务,并等待它们全部完成后才会返回结果。也就是说,这种方式只有在所有任务都执行完成后,才会进入下一步。

await asyncio.wait([producer(queue), consumer(queue)]) 则是将多个协程对象(通过列表进行传递)传递给 asyncio.wait() 函数,这个函数也会等待多个协程同时完成。不同的是,asyncio.wait() 函数会返回两个集合(即 done 和 pending),done 集合包含已经完成的任务,pending 集合包含还未完成的任务。因此可以通过遍历 done 集合取得协程的结果。

总的来说,asyncio.gather() 更加简单易用,适合并发执行多个任务并等待它们全部完成的情况。而 asyncio.wait() 则更加灵活,并且可以实时获取任务的执行结果。

3. 启动方式差异

注意,在 Python 3.7 以前,我们需要使用 asyncio.get_event_loop().run_until_complete() 函数来运行协程任务。但在 Python 3.7 及以后的版本中,我们可以使用更为简洁的 asyncio.run() 函数来运行协程任务。
示例代码如下:

import asyncio
import random


async def producer(queue):
    while True:
        value = random.randint(0, 10)
        print(f"Produced: {value}")
        await queue.put(value)
        await asyncio.sleep(random.random())

async def consumer(queue):
    while True:
        value = await queue.get()
        print(f"Consumed: {value}")
        await asyncio.sleep(random.random())


async def main():
    queue = asyncio.Queue()
    '''方法一'''
    # task_producer = asyncio.create_task(producer(queue))
    # task_consumer = asyncio.create_task(consumer(queue))
    # await asyncio.gather(task_producer, task_consumer)
    '''方法二'''
    await asyncio.wait([producer(queue), consumer(queue)])


'''协程启动的方式一'''
# asyncio.run(main())

'''协程启动的方式二'''
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
new_loop.run_until_complete(main())

总之,协程是一种高效、轻量级的并发编程方式,能够提高程序的性能,简化异步编程,使得控制流程更加灵活,是 Python 中重要的并发编程工具之一。

以上就是关于Python - 协程基本使用的介绍,希望对你有所帮助,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/895378.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

外卖福利来了,以后都10元以下了,还有机会赚钱,你信吗

怎么实现的点外卖赚钱 在外卖返现平台抢单,用信用卡支付订单,上传好评返现 外卖返现平台是看订单金额,信用卡满减不计入其中,这样就有机会实现赚钱 外卖返现平台 用了半年多返现5000多了,也是一笔开支了 扫最后面的…

快递打单系统使用教程

旅游旺季游客太多怎么办? 相信不少景区特产店都有这种“甜蜜的困扰”。一方面游客多了,自然销售量见长,另一面人流多了,如何服务好顾客,也是特产店的一大难题。 客户询价,太忙没能第一时间回复&#xff1…

Linux15 消息队列 线程

目录 1、进程间通信IPC: 2、多线程 3、向消息队列中写入数据 4、从消息队列中读取数据 5、多线程: 6、将多线程的数据返回给主…

js实现瀑布流布局

jquery.masonry.min.js:https://download.csdn.net/download/weixin_45791806/88224671 jQeasing.js: https://download.csdn.net/download/weixin_45791806/88224673 jquery.lazyload.js这个js可以自己百度下载 直通车:https://download.csd…

【JAVA】我们该如何规避代码中可能出现的错误?(一)

个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 文章目录 前言三种类型的异常异常处理JAVA内置异常类Exception 类的层次 前言 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的&…

设计模式之中介者模式(Mediator)的C++实现

1、中介者模式的提出 在软件组件开发过程中,如果存在多个对象,且这些对象之间存在的相互交互的情况不是一一对应的情况,这种功能组件间的对象引用关系比较复杂,耦合度较高。如果有一些新的需求变化,则不易扩展。中介者…

好用的语音转文字免费app手机软件分享给你

嘿!你有没有遇到过这样的情况:听到了一段精彩的演讲、访谈,但又不方便记录下来?或者,你是一个繁忙的职场人士,需要快速将会议内容转化为文字记录,又苦于手动转写花费时间太多?别担心&#xff0c…

使用Alien对.deb包与.rpm包相互转换

目录 1、切换到root 2、更新yum(更新比较耗时,不更新没试行不,自行斟酌是否跳过这一步) 3、卸载ibus 4、安装Alien及其依赖包 5、安装Alien 6、将.deb转换成.rpm包 7、安装RPM包 8、如果报错 9、将.rpm转换成.deb包 10、安…

白嫖怪小案例———用爬虫实现csdn免费下载资源搜寻

前言 众所周知,在csdn下载资源有很多都是要收费的,最常见的是要积分的 但是小编囊中羞涩,买不起VIP,也没有积分,而资源又要一个一个点进去才知道是不是免费的(最爱0积分了,老白嫖怪了&#xff…

Egg.js构建一个stream流式接口服务

经常需要用到 stream 流式接口服务,比如:大文件下载、日志实时输出等等。本文将介绍如何使用Egg.js构建一个 stream 流式接口服务。 一、准备工作 目录结构: app//controllerindex.jstest.txttest.shindex.js 控制器test.txt 测试文件,最好…

vue3 injection报错 injection“xxx“ not found.

在封装CheckboxGroup组件的的时候&#xff0c;需要通过provide&#xff0c;代码如下&#xff1a; //父组件 <template><div class"envCheckBoxGroup"><slot></slot></div> </template> <script setup> import { provide …

【云原生】3分钟快速在Kubernetes部署Prometheus2.42+Grafana9.5.1+Alertmanager0.25

文章目录 1、简介2、GitHub地址3、环境信息4、安装5、访问Grafana1、简介 Prometheus-operator帮助我们快速创建Prometheus+Grafana+Alertmanager等服务,而kube-prometheus更加完整的帮助我们搭建全套监控体系,这包括部署多个 Prometheus 和 Alertmanager 实例, 指标导出器…

Spring系列篇--关于Spring Bean完整的生命周期【附有流程图,超级易懂】

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Spring的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Spring Bean是单例模式还是多例模式 二…

【脚踢数据结构】查找

(꒪ꇴ꒪ )&#xff0c;Hello我是祐言QAQ我的博客主页&#xff1a;C/C语言&#xff0c;Linux基础&#xff0c;ARM开发板&#xff0c;软件配置等领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff0c;让我们成为一个强大的攻城狮&#xff01;送给自己和读者的…

代码随想录—力扣算法题:707设计链表.Java版(示例代码与导图详解)

版本说明 当前版本号[20230818]。 版本修改说明20230818初版 目录 文章目录 版本说明目录707.设计链表思路获取链表第index个节点的数值在链表的最前面插入一个节点在链表的最后面插入一个节点在链表第index个节点前面插入一个节点删除链表的第index个节点 单链表角度总结 7…

STM32入门——IIC通讯

江科大STM32学习记录 I2C通信 I2C&#xff08;Inter IC Bus&#xff09;是由Philips公司开发的一种通用数据总线两根通信线&#xff1a;SCL&#xff08;Serial Clock&#xff09;、SDA&#xff08;Serial Data&#xff09;同步&#xff0c;半双工带数据应答支持总线挂载多设备…

Python“牵手”lazada商品评论数据采集方法,lazadaAPI申请指南

lazada平台API接口是为开发电商类应用程序而设计的一套完整的、跨浏览器、跨平台的接口规范&#xff0c;lazadaAPI接口是指通过编程的方式&#xff0c;让开发者能够通过HTTP协议直接访问lazada平台的数据&#xff0c;包括商品信息、店铺信息、物流信息等&#xff0c;从而实现la…

程序人生:进不了大厂的测试员,究竟还有没有出路了?

金九银十的到来&#xff0c;使得许多职场人按耐不住&#xff0c;纷纷开始找寻合适的工作机会。猎头和HR们摩拳擦掌&#xff0c;争取在这两个月给今年多加点业绩。 对许多互联网人来说&#xff0c;跳槽意味着加薪&#xff0c;而对于程序员而言&#xff0c;是否能跳槽进大厂是他…

前端---需要了解浏览器相关知识--浏览器请求服务器资源---缓存

知识点1: 掘金1&#xff1a;浏览器缓存 掘金2 :浏览器缓存 一、浏览器缓存 请求&#xff08;静态资源 &#xff5c; 动态资源&#xff09; 一、缓存是什么&#xff1f; 如果没有缓存的机制 每次都要重新请求静态资源 1.从网络上的下载时间&#xff0c;肯定大于从硬盘里读的…

《树莓派4B家庭服务器搭建指南》第二十期:在树莓派运行rsnapshot, 实现对服务器数据低成本增量本地备份

title: 020《树莓派4B家庭服务器搭建指南》第二十期&#xff1a;在树莓派运行rsnapshot, 实现对服务器数据低成本增量本地备份 我的天翼云服务器有/opt 和 /usr/share/nginx两个目录, 用来存储网站的内容, 数据无价, 为了避免珍贵的数据丢失&#xff0c;我决定使用树莓派运行 …