协程 / 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. [Leetcode] distinct subsequences 不同子序列

    Given a string S and a string T, count the number of distinct subsequences of T in S. A subsequence ...

  2. 十个迅速提升JQuery性能的技巧

    本文提供即刻提升你的脚本性能的十个步骤.不用担心,这并不是什么高深的技巧.人人皆可运用!这些技巧包括: 使用最新版本 合并.最小化脚本 用for替代each 用ID替代class选择器 给选择器指定前 ...

  3. HttpClientUntils工具类的使用测试及注意事项(包括我改进的工具类和Controller端的注意事项【附 Json 工具类】)

    HttpClient工具类(我改过): package com.taotao.httpclient; import java.io.IOException; import java.net.URI; ...

  4. Java之戳中痛点 - (5)switch语句break不能忘以及default不同位置的用法

    先看一段代码: public class Test{ public static void main(String[] args){ System.)); } } public static Stri ...

  5. WebKit学习资源

    1.http://blog.csdn.net/dlmu2001/article/category/741748    红心地瓜 2.http://blog.csdn.net/cnnzp/article ...

  6. SpringMVC学习 -- 使用 @RequestMapping 映射请求

    在控制器的类定义及方法出定义出都可以标注 @RequestMapping: 类定义处:提供初步的请求映射信息.相对于 Web 应用的根目录. 方法定义出:提供进一步的细分映射信息.相对于类定义处的 U ...

  7. es6+最佳入门实践(5)

    5.对象扩展 5.1.对象简写 在es5中,有这样一种写法 var name = "xiaoqiang"; var age = 12; var obj = { name : nam ...

  8. struts2学习问题(一)

    一.struts2 Unknown tag (s:property). 解释:不识别标签 解决:这是sturts2的标签,导入相应的包<%@taglib prefix="s" ...

  9. 【CF103D】Time to Raid Cowavans(分块)

    题意: 思路:院赛防AK题,然而还没来得及做就被数据出锅的题坑了…… #include<cstdio> #include<cstring> #include<string ...

  10. (十二)进一步掌握STVD/COSMIC

    如何分配变量到指定的地址 举例:unsigned char temp_A@0x00; //定义无符号变量temp_A,强制其地址为0x00unsigned char temp_B@0x100; //定 ...