协程 / Coroutine


目录

  1. 生产者消费者模型
  2. 从生成器到异步协程– async/await

协程是在一个线程执行过程中可以在一个子程序的预定或者随机位置中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。它本身是一种特殊的子程序或者称作函数。

一个程序可以包含多个协程,可以对比与一个进程包含多个线程。我们知道多个线程相对独立,有自己的上下文,切换受系统控制;而协程也相对独立,有自己的上下文,但是其切换由自己控制,由当前协程切换到其他协程由当前协程来控制。

1 生产者消费者模型

下面以一个例子介绍一个简单的协程实现,

首先,模拟生产者和消费者模型,建立一个消费者函数,接收一个参数为传入的生产者,初始时使用next函数或send(None)来启动,然后连续7次调用send,将程序切入生产者answer,获取结果,最后调用close或send(None)来结束协程。

 import time

 def ask(a):
next(a) # Start generator
# a.send(None)
n = 0
while n < 7: # Ask certain number questions then exit.
print('Ask: Try to ask question %d' % n)
r = a.send(n) # Send Ques number (ask question), receive is r
print('Ask: Received answer <%s>' % r)
n += 1
a.close() # End loop
# try:
# a.send(None)
# except StopIteration as e:
# pass

接下来定义一个生产者answer,生产者会不停返回结果,除非收到None或被调用close函数从而结束。

 def answer():   # Answer generator
ans = '' # First answer for generator start
while True:
qus = yield ans # Return answer
if qus is None:
return
print('Answer: Received question %s' % qus)
time.sleep(1)
ans = 'Best answer' ask(answer())

运行得到结果,

Ask: Try to ask question 0
Answer: Received question 0
Ask: Received answer <Best answer>
Ask: Try to ask question 1
Answer: Received question 1
Ask: Received answer <Best answer>
Ask: Try to ask question 2
Answer: Received question 2
Ask: Received answer <Best answer>
Ask: Try to ask question 3
Answer: Received question 3
Ask: Received answer <Best answer>
Ask: Try to ask question 4
Answer: Received question 4
Ask: Received answer <Best answer>
Ask: Try to ask question 5
Answer: Received question 5
Ask: Received answer <Best answer>
Ask: Try to ask question 6
Answer: Received question 6
Ask: Received answer <Best answer>

可以看到,ask和answer之间完成了协作性任务,同一时间自由一个线程在执行,不存在线程的切换。

2 从生成器到异步协程– async/await

在Python中,生成器和协程总是难以区别,为此,在Python3.5之后,引入了新的关键字async和await,用于将普通的函数或生成器包装成为异步的函数和生成器。

下面用代码展示如何使用生成器和协程完成一个异步操作,

完整代码

 #!/usr/bin/python
# =============================================================
# File Name: gene_to_coro.py
# Author: LI Ke
# Created Time: 1/29/2018 15:34:50
# ============================================================= print('-------- Generator ----------') def switch_1():
print('Switch_1: Start')
yield
print('Switch_1: Stop') def switch_2():
print('Switch_2: Start')
yield
print('Switch_2: Stop') a = switch_1()
b = switch_2()
a.send(None)
b.send(None)
try:
b.send(None)
except StopIteration as e:
re = e.value try:
a.send(None)
except StopIteration as e:
re = e.value print('-------- Async Coro ----------') async def switch_1():
print('Switch_1: Start')
await switch_2()
print('Switch_1: Stop') async def switch_2():
print('Switch_2: Start')
print('Switch_2: Stop') a = switch_1()
try:
a.send(None)
except StopIteration as e:
re = e.value

分段解释

首先利用生成器来完成一个异步操作,定义两个生成器,分别在启动后yield出当前环境,

 print('-------- Generator ----------')

 def switch_1():
print('Switch_1: Start')
yield
print('Switch_1: Stop') def switch_2():
print('Switch_2: Start')
yield
print('Switch_2: Stop')

完成生成器后,首先分别实例化两个生成器,并利用send(None)进行启动,启动a后再启动b,随后再切入b中完成剩余操作,当b完成后捕获StopIteration异常,并再次切入a中完成后续的操作。

 a = switch_1()
b = switch_2()
a.send(None)
b.send(None)
try:
b.send(None)
except StopIteration as e:
re = e.value try:
a.send(None)
except StopIteration as e:
re = e.value

最终运行结果为,

-------- Generator ----------
Switch_1: Start
Switch_2: Start
Switch_2: Stop
Switch_1: Stop

可以看到,利用生成器完成了一个预先设定好的运行流程,仅仅利用单线程完成了一个异步切换的协作式任务。

可是上面的方式存在一个问题,即整个程序的结构十分松散,逻辑上难以理清,因此下面用新增的关键字async和await来完成一个更加符合思维逻辑的异步流程。

首先定义两个异步协程,在协程1中,当协程1开始后,利用await显式地切换至协程2中,当协程2完成后,又继续执行协程1中的操作,整个协程异步的工作顺序在协程内便完成,因此在外部仅需要启动协程1即可。

 print('-------- Async Coro ----------')

 async def switch_1():
print('Switch_1: Start')
await switch_2()
print('Switch_1: Stop') async def switch_2():
print('Switch_2: Start')
print('Switch_2: Stop') a = switch_1()
try:
a.send(None)
except StopIteration as e:
re = e.value

最后得到的结果与前面利用生成器方式得到的结果相同,但却以一种更加清晰的方式完成了异步编程。

-------- Async Coro ----------
Switch_1: Start
Switch_2: Start
Switch_2: Stop
Switch_1: Stop

Python的异步编程[0] -> 协程[0] -> 协程和 async / await的更多相关文章

  1. JavaScript 如何工作的: 事件循环和异步编程的崛起 + 5 个关于如何使用 async/await 编写更好的技巧

    原文地址:How JavaScript works: Event loop and the rise of Async programming + 5 ways to better coding wi ...

  2. 深入理解协程(四):async/await异步爬虫实战

    本文目录: 同步方式爬取博客标题 async/await异步爬取博客标题 本片为深入理解协程系列文章的补充. 你将会在从本文中了解到:async/await如何运用的实际的爬虫中. 案例 从CSDN上 ...

  3. Python的异步编程[0] -> 协程[1] -> 使用协程建立自己的异步非阻塞模型

    使用协程建立自己的异步非阻塞模型 接下来例子中,将使用纯粹的Python编码搭建一个异步模型,相当于自己构建的一个asyncio模块,这也许能对asyncio模块底层实现的理解有更大的帮助.主要参考为 ...

  4. python 之 并发编程(线程Event、协程)

    9.14 线程Event connect线程执行到event.wait()时开始等待,直到check线程执行event.set()后立即继续线程connect from threading impor ...

  5. Python的网络编程[2] -> TFTP 协议[0] -> TFTP 的基本理论

    TFTP 的基本理论 目录 通信流程 数据报文格式 传输终结 异常处理 数据丢失和超时 TFTP(Trivial File Transfer Protocol,简单文件传输协议)是UDP协议族中的一个 ...

  6. python之异步编程

    一.异步编程概述 异步编程是一种并发编程的模式,其关注点是通过调度不同任务之间的执行和等待时间,通过减少处理器的闲置时间来达到减少整个程序的执行时间:异步编程跟同步编程模型最大的不同就是其任务的切换, ...

  7. 深入理解协程(三):async/await实现异步协程

    原创不易,转载请联系作者 深入理解协程分为三部分进行讲解: 协程的引入 yield from实现异步协程 async/await实现异步协程 本篇为深入理解协程系列文章的最后一篇. 从本篇你将了解到: ...

  8. [C#] .NET4.0中使用4.5中的 async/await 功能实现异步

    在.NET Framework 4.5中添加了新的异步操作库,但是在.NET Framework 4.0中却无法使用.这时不免面临着抉择,到底是升级整个解决方案还是不使用呢? 如果你的软件还没发布出去 ...

  9. .NET4.0中使用4.5中的 async/await 功能实现异步

    在.NET Framework 4.5中添加了新的异步操作库,但是在.NET Framework 4.0中却无法使用.这时不免面临着抉择,到底是升级整个解决方案还是不使用呢? 如果你的软件还没发布出去 ...

随机推荐

  1. jQuery插件jquery.fullPage.js

    简介如今我们经常能看到全屏网站,尤其是国外网站.这些网站用几幅很大的图片或者色块做背景,再添加一些简单的内容,显得格外的高端大气上档次,比如 iPone 5C 的介绍页面.QQ浏览器的官方网站.百度史 ...

  2. 2017 Multi-University Training Contest - Team 3 RXD and functions(NTT)

    题解: 我是参考的 http://blog.csdn.net/qq_32570675/article/details/76571666 这一篇 orz 原来可以这么变换,涨姿势 代码: #includ ...

  3. 斜率dp+cdq分治

    写在前面 这个东西应该是一个非常重要的套路......所以我觉得必须写点什么记录一下,免得自己忘掉了 一直以来我的斜率dp都掌握的不算很好......也很少主动地在比赛里想到 写这个的契机是noi.a ...

  4. 【ZJ选讲·BZOJ 5073】

    小A的咒语 给出两个字符串A,B (len<=105) 现在可以把A串拆为任意段,然后取出不超过 x 段,按在A串中的前后顺序拼接起来 问是否可以拼出B串. [题解]       ①如果遇 ...

  5. jsonArray与jsonObject

    最近两个星期接触最多的就是json和map了. 之前用到的json,就是一个键对应一个值,超级简单的一对一关系.现在用到的json那可以层层嵌套啊,刚开始接触的时候,确实有种崩溃的赶脚,不想去理,取个 ...

  6. 转:深入理解javascript原型和闭包系列

    转自:深入理解javascript原型和闭包系列 从下面目录中可以看到,本系列有16篇文章,外加两篇后补的,一共18篇文章.写了半个月,从9月17号开始写的.每篇文章更新时,读者的反馈还是可以的,虽然 ...

  7. 如何使用SDK在Ubuntu设备(包括仿真器和桌面)上运用应用程序

    简介 有三种运行通过SDK创建的应用程序的方式:在桌面上,在联网的Ubuntu设备上,以及在仿真器中.这些方式为互补性方式,因为各有优缺点.您首先将了解如何管理SDK的设备类型,以及哪一个类型用于测试 ...

  8. 图像转换为二进制文件存入DSP6748

    本文为原创作品,转载请注明出处 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing/ 这篇博客主 ...

  9. Selenium菜鸟手册

    转自: http://www.iselenium.com/read.php?tid=458 首先声明我还是一只很菜的菜鸟,学习Selenium一个来月而已,发这个帖子是想利用我这块板砖引出真正的玉来, ...

  10. UVALIVE 2954 Task Sequences

    竞赛图:图中的任意两点间有且仅有一条有向弧连接 求竞赛图中的哈密顿路的算法: 首先,由数学归纳法可证竞赛图在n>=2时必存在哈密顿路: (1)n=2时显然: (2)假设n=k时,结论成立,哈密顿 ...