协程 / 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. BZOJ4367 IOI2014holiday假期(整体二分+主席树)

    显然最优策略是先走到一边要到达的最远城市,再换方向走到另一边要到达的最远城市(当然也可以直接停止),路上参观景点. 先仅考虑求出只向左走,花费时间i时的最优解.如果能求出这个,类似的就可以求出所有情况 ...

  2. Tomcat-Jdbc-Pool连接池参数说明

    原文  http://liuxing.info/2016/01/05/Tomcat-Jdbc-Pool参数说明/ 转载收藏用,如有侵权,请联系删除,谢谢. 介绍 Tomcat 在 7.0 以前的版本都 ...

  3. android脱壳之DexExtractor原理分析

    导语: 上一篇我们分析android脱壳使用对dvmDexFileOpenPartial下断点的原理,使用这种方法脱壳的有2个缺点: 1.  需要动态调试 2.  对抗反调试方案 为了提高工作效率, ...

  4. <video>标签的特性

    以前的网页视频 过去还没有HTML5的时候,我们处理网页视频的时候,都是通过Flash插件来实现的,然而,并非所有浏览器都有同样的插件. 现在有了HTML5带来的video元素,开发者能够很方便地将视 ...

  5. VS2010 VC Project的default Include设置

    在IDE中,打开View->Other Windows->Property Manager.展开树形后,你会发现一个名为“Microsoft.Cpp.Win32.user”的项目(如下图) ...

  6. Centos7下redis设置密码、开放远程访问权限

    redis的安装与启动可参考前一篇文章:http://www.cnblogs.com/zuidongfeng/p/8032505.html redis安装成功后,默认是没有设置密码的启动redis-c ...

  7. css中文本超出部分省略号代替

    p{ width: 100px; //设置p标签宽度 white-space: nowrap; //文本超出P标签宽度不换行,而是溢出 overflow: hidden; //文本超出P标签,超出部分 ...

  8. C/C++常考面试题(二)

    网上看到的面经,说是dynamic_cast的实现,和RTTI的相关,这才发现原来对这个概念这么模糊,所以作了这个总结. C/C++常考面试题(二) RTTI(Runtime Type Informa ...

  9. 【数据结构】bzoj2957楼房重建

    Description 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些 ...

  10. Topcoder SRM 607 div1题解

    好久没来写了,继续继续... Easy(250pts): //前方请注意,样例中带有zyz,高能预警... 题目大意:给你一个字符串,中间有一些是未知字符,请你求出这个字符串的回文子串个数的期望值.数 ...