• Python的异步编程[0] -> 协程[0] -> 协程和 async / await


    协程 / Coroutine


    目录

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

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

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

    1 生产者消费者模型

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

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

     1 import time
     2 
     3 
     4 def ask(a):
     5     next(a)    # Start generator
     6     # a.send(None)
     7     n = 0
     8     while n < 7:    # Ask certain number questions then exit.
     9         print('Ask: Try to ask question %d' % n)    
    10         r = a.send(n)       # Send Ques number (ask question), receive is r
    11         print('Ask: Received answer <%s>' % r)
    12         n += 1
    13     a.close()   # End loop
    14     # try:
    15     #     a.send(None)
    16     # except StopIteration as e:
    17     #     pass

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

     1 def answer():   # Answer generator
     2     ans = ''    # First answer for generator start
     3     while True:
     4         qus = yield ans     # Return answer
     5         if qus is None:
     6             return
     7         print('Answer: Received question %s' % qus)
     8         time.sleep(1)
     9         ans = 'Best answer'
    10 
    11 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>
    View Code

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

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

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

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

    完整代码

     1 #!/usr/bin/python
     2 # =============================================================
     3 # File Name: gene_to_coro.py
     4 # Author: LI Ke
     5 # Created Time: 1/29/2018 15:34:50
     6 # =============================================================
     7 
     8 
     9 print('-------- Generator ----------')
    10 
    11 def switch_1():
    12     print('Switch_1: Start')
    13     yield
    14     print('Switch_1: Stop')
    15 
    16 
    17 def switch_2():
    18     print('Switch_2: Start')
    19     yield
    20     print('Switch_2: Stop')
    21 
    22 a = switch_1()
    23 b = switch_2()
    24 a.send(None)
    25 b.send(None)
    26 try:
    27     b.send(None)
    28 except StopIteration as e:
    29     re = e.value
    30 
    31 try:
    32     a.send(None)
    33 except StopIteration as e:
    34     re = e.value
    35 
    36 print('-------- Async Coro ----------')
    37 
    38 async def switch_1():
    39     print('Switch_1: Start')
    40     await switch_2()
    41     print('Switch_1: Stop')
    42 
    43 async def switch_2():
    44     print('Switch_2: Start')
    45     print('Switch_2: Stop')
    46 
    47 a = switch_1()
    48 try:
    49     a.send(None)
    50 except StopIteration as e:
    51     re = e.value
    View Code

    分段解释

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

     1 print('-------- Generator ----------')
     2 
     3 def switch_1():
     4     print('Switch_1: Start')
     5     yield
     6     print('Switch_1: Stop')
     7 
     8 
     9 def switch_2():
    10     print('Switch_2: Start')
    11     yield
    12     print('Switch_2: Stop')

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

     1 a = switch_1()
     2 b = switch_2()
     3 a.send(None)
     4 b.send(None)
     5 try:
     6     b.send(None)
     7 except StopIteration as e:
     8     re = e.value
     9 
    10 try:
    11     a.send(None)
    12 except StopIteration as e:
    13     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即可。

     1 print('-------- Async Coro ----------')
     2 
     3 async def switch_1():
     4     print('Switch_1: Start')
     5     await switch_2()
     6     print('Switch_1: Stop')
     7 
     8 async def switch_2():
     9     print('Switch_2: Start')
    10     print('Switch_2: Stop')
    11 
    12 a = switch_1()
    13 try:
    14     a.send(None)
    15 except StopIteration as e:
    16     re = e.value

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

    -------- Async Coro ----------
    Switch_1: Start
    Switch_2: Start
    Switch_2: Stop
    Switch_1: Stop
  • 相关阅读:
    SpringMVC:JSON讲解
    SpringMVC:文件上传和下载
    字符串的使用
    python中的作用域与名称空间
    深、浅copy
    代码块与小数据池之间的关系
    关于敏感字符的筛选替换
    列表的增、删、改、查
    最简三级菜单
    python2.x与python3.x的区别
  • 原文地址:https://www.cnblogs.com/stacklike/p/8379249.html
Copyright © 2020-2023  润新知