中gather函数_深入理解Python中的协程

f6d9878e137a9185ed492cdd71397d2a.png @Author:By Runsen一说并发,你肯定想到了多线程+进程模型,确实,多线程+进程,正是解决并发问题的经典模型之一。但对于多核CPU,利用多进程+协程的方式,能充分利用CPU,获得极高的性能。协程也是实现并发编程的一种方式。

协程

协程:是单线程下的并发,又称微线程。英文名是Coroutine。它和线程一样可以调度,但是不同的是线程的启动和调度需要通过操作系统来处理。协程是一种比线程更加轻量级的存在,最重要的是,协程不被操作系统内核管理,协程是完全由程序控制的。运行效率极高,协程的切换完全由程序控制,不像线程切换需要花费操作系统的开销,线程数量越多,协程的优势就越明显。协程不需要多线程的锁机制,因为只有一个线程,不存在变量冲突。对于多核CPU,利用多进程+协程的方式,能充分利用CPU,获得极高的性能。注意协程这个概念完全是程序员自己想出来的东西,它对于操作系统来说根本不存在。操作系统只有进程和线程。

Python中使用协程的例子

yield关键字相当于是暂停功能,程序运行到 yield停止, send函数可以传参给生成器函数,参数赋值给 yield
def customer():while True:
        number = yieldprint('开始消费:',number)
custom = customer()
next(custom)for i in range(5):print('开始生产:',i)
    custom.send(i)
结果如下
开始生产: 0
开始消费: 0
开始生产:1
开始消费:1
开始生产:2
开始消费:2
开始生产:3
开始消费:3
开始生产:4
开始消费:4
代码解析:
  1. 协程使用生成器函数定义:定义体中有 yield 关键字。
  2. yield 在表达式中使用;如果协程只需从客户custom接收数据,如果没有产出的值,那么产出的值是 None。
  3. 首先要调用 next(…) 函数,因为生成器还没启动,没在 yield 语句处暂停,所以一开始无法发送数据。
  4. 调用send方法,把值传给 yield 的变量,然后协程恢复,继续执行下面的代码,直到运行到下一个 yield 表达式,或者终止。

async和await

async和await是原生协程,是Python3.5以后引入的两个关键词。 async 是“异步”的简写,而 await 可以认为是 async wait 的简写。所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。下面,我们从一个demo示例看起,具体代码如下。
import time 
def print_num(num):print("Maoli is printing " + str(num) + " nows" )
    time.sleep(1)print("Maoli prints" + str(num) + " OK")
def main(nums):for num in nums:print_num(num)
%time main([i for i in range(1,6)])
Maoli is printing 1 nows
Maoli prints1 OK
Maoli is printing 2 nows
Maoli prints2 OK
Maoli is printing 3 nows
Maoli prints3 OK
Maoli is printing 4 nows
Maoli prints4 OK
Maoli is printing 5 nows
Maoli prints5 OK
Wall time: 5 s
%time 需要在jupyter notebook中运行,这是jupyter的语法糖。上面代码是从上到下执行的。下面我们将上面的代码改成单线程协程版本。 「注意py版本3.7以上」,主要使用的是asyncio模块,如果出现 AttributeError: module ‘asyncio‘ has no attribute ‘run‘报错,这是asyncio版本不兼容的原因,需要将Python版本提升至3.7以上。
import asyncio
async def print_num(num):print("Maoli is printing " + str(num) + " nows" )
    await asyncio.sleep(1)print("Maoli prints" + str(num) + " OK")
async def main(nums):for num in nums:
        await print_num(num)
%time asyncio.run(main([i for i in range(1,6)]))
Maoli is printing 1 nows
Maoli prints1 OK
Maoli is printing 2 nows
Maoli prints2 OK
Maoli is printing 3 nows
Maoli prints3 OK
Maoli is printing 4 nows
Maoli prints4 OK
Maoli is printing 5 nows
Maoli prints5 OK
Wall time: 5.01 s
asyncio.run() 函数用来运行最高层级的入口点 "main()" 函数。await 是同步调用等待一个协程。以下代码段会在等待 1 秒后打印 num,但在运行速度上没有发生改变。这里需要引入 asyncio.create_task可等待对象才可以。

create_task

如果一个对象可以在 await 语句中使用,那么它就是可等待对象。协程中的还一个重要概念,任务(Task)。如果写一个数字是一个任务,那么毛利我要完成5个任务。毛利我写个1-5都这么慢,不行,我要加速写。 asyncio.create_task() 函数用来并发运行作为 asyncio 任务 的多个协程。
import asyncio
async def print_num(num):print("Maoli is printing " + str(num) + " nows" )
    await asyncio.sleep(1)print("Maoli prints" + str(num) + " OK")
async def main(nums):
    tasks = [asyncio.create_task(print_num(num)) for num in nums]for task in tasks:
        await task
%time asyncio.run(main([i for i in range(1,6)]))
Maoli is printing 1 nows
Maoli is printing 2 nows
Maoli is printing 3 nows
Maoli is printing 4 nows
Maoli is printing 5 nows
Maoli prints1 OK
Maoli prints3 OK
Maoli prints5 OK
Maoli prints2 OK
Maoli prints4 OK
Wall time: 1.01 s
还可以写成 await asyncio.gather(*tasks)这种方法
import asyncio
async def print_num(num):print("Maoli is printing " + str(num) + " nows" )
    await asyncio.sleep(1)print("Maoli prints" + str(num) + " OK")
async def main(nums):
    tasks = [asyncio.create_task(print_num(num)) for num in nums]
    await asyncio.gather(*tasks)
%time asyncio.run(main([i for i in range(1,6)]))
*tasks 解包列表,将列表变成了函数的参数;与之对应的是,   ** dict 将字典变成了函数的参数。协程的写法简洁清晰,只要把 async / await 语法和 create_task 结合来用,就是Python中比较常见的协程写法。今天也学到了很多东西呢,明天有什么新知识呢?真期待鸭~如果喜欢文章可以关注我哦~
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值