zoukankan      html  css  js  c++  java
  • Python3 协程相关

    什么是协程

      协程(Coroutine),又称微线程,纤程。通常我们认为线程是轻量级的进程,因此我们也把协程理解为轻量级的线程即微线程。

      协程的作用是在执行函数A时可以随时中断去执行函数B,然后中断函数B继续执行函数A(可以自由切换)。这里的中断,不是函数的调用,而是有点类似CPU的中断。这一整个过程看似像多线程,然而协程只有一个线程执行。

    协程的优势

    • 执行效率极高,因为是子程序(函数)切换不是线程切换,由程序自身控制,没有切换线程的开销。所以与多线程相比,线程的数量越多,协程的性能优势越明显。
    • 不需要机制,因为只有一个线程,也不存在同时写变量冲突,在控制共享资源时也不需要加锁,只需要判断状态,因此执行效率高的多。
    • 协程可以处理IO密集型程序的效率问题,但不适合处理CPU密集型程序,如要充分发挥CPU利用率应结合多进程+协程。

    Python3中的协程

    生成器 yield/send

    yield + send(利用生成器实现协程)

    例:

    def simple_coroutine():
        print('-> start')
        x = yield
        print(x)
        print('-> end')
    
    #主线程
    sc = simple_coroutine()
    # 可以使用sc.send(None),效果一样
    next(sc) #预激
    sc.send('go')
    

    执行结果如下:

    -> start
    go
    -> end
    
    抛出StopIteration
    
    • simple_coroutine()是一个生成器,由next(sc) 预激,启动协程,执行到第一个yield中断
    • send()方法给yield传入参数,继续执行

    协程的四个状态

    • GEN_CREATED:等待开始执行
    • GEN_RUNNING:解释器正在执行
    • GEN_SUSPENED:在yield表达式处暂停
    • GEN_CLOSED:执行结束

    协程终止

    • 协程中未处理的异常会向上冒泡,传给 next 函数或 send 方法的调用方(即触发协程的对象)
    • 终止协程的一种方式:发送某个哨符值,让协程退出。内置的 None 和Ellipsis 等常量经常用作哨符值

    @asyncio.coroutine和yield from

    asyncio.coroutione

      asyncio是Python3.4版本引入的标准库,直接内置了对异步IO的支持。asyncio的异步操作,需要在coroutine中通过yield from完成。
    例:

    import asyncio
    @asyncio.coroutine
    def test(i):
        print('test_1', i)
        r = yield from asyncio.sleep(1)
        print('test_2', i)
    
    loop = asyncio.get_event_loop()
    tasks = [test(i) for i in range(1,3)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    
    • @asyncio.coroutine把一个generator标记为coroutine类型,然后就把这个coroutine扔到EventLoop中执行。test()会首先打印出test_1
    • 然后yield from语法可以让我们方便地调用另一个generator。由于asyncio.sleep()也是一个coroutine,所以线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。
    • asyncio.sleep()返回时,线程就可以从yield from拿到返回值(此处是None)
    • 然后接着执行下一行语句。把asyncio.sleep(1)看成是一个耗时1秒的IO操作,在此期间主线程并未等待,而是去执行EventLoop中其他可以执行的coroutine了,因此可以实现并发执行

    结果如下:

    test_1 1
    test_1 2
    test_2 1
    test_2 2
    

    yield from

      当yield from 后面加上一个生成器后,就实现了生成的嵌套。
      实现生成器的嵌套,并不是一定必须要使用yield from,而是使用yield from可以让我们避免让我们自己处理各种料想不到的异常,而让我们专注于业务代码的实现。
      如果自己用yield去实现,那只会加大代码的编写难度,降低开发效率,降低代码的可读性。

    • 调用方:调用委派生成器的客户端(调用方)代码
    • 委托生成器:包含yield from表达式的生成器函数
    • 子生成器:yield from后面加的生成器函数
    # 子生成器
    def average_gen():
        total = 0
        count = 0
        average = 0
        while True:
            new_num = yield average
            count += 1
            total += new_num
            average = total/count
    
    # 委托生成器
    def proxy_gen():
        while True:
            yield from average_gen()
    
    # 调用方
    def main():
        calc_average = proxy_gen()
        next(calc_average)# 预激下生成器
        print(calc_average.send(10))  
        print(calc_average.send(20))  
        print(calc_average.send(30))  
    
    main()
    
    

    结果如下:

    10.0
    15.0
    20.0
    

    委托生成器的作用:
    在调用方与子生成器之间建立一个双向通道。

    双向通道就是调用方可以通过send()直接发送消息给子生成器,而子生成器yield的值,也是直接返回给调用方。

    为什么要用yield from

      yield from帮我们做了很多的异常处理,而这些如果我们要自己去实现的话,一个是编写代码难度增加,写出来的代码可读性很差,很可能有遗漏,只要哪个异常没考虑到,都有可能导致程序崩溃。

    async/await关键字

      为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法async和await,可以让coroutine的代码更简洁易读。

    可将上面的案例代码改造如下:

    import asyncio
    # @asyncio.coroutine
    async def test(i):
        print('test_1', i)
        # r = yield from asyncio.sleep(1)
        await asyncio.sleep(1)
        print('test_2', i)
    
    loop = asyncio.get_event_loop()
    tasks = [test(i) for i in range(1,3)]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()
    

    执行结果

    test_1 1
    test_1 2
    test_2 1
    test_2 2
    
    • async 替换 @asyncio.coroutine,加在def之前用于修饰
    • await替换yield from

  • 相关阅读:
    Git详解之四 服务器上的Git
    Git详解之三 Git分支
    Git详解之二 Git基础
    Git详解之一 Git起步
    Spring Data JPA 梳理
    Spring Data JPA 梳理
    Spring boot 梳理
    Apache和Tomcat 配置负载均衡(mod-proxy方式)-粘性session
    Apache和Tomcat 配置负载均衡(mod-proxy方式)-无session共享、无粘性session
    Spring boot 官网学习笔记
  • 原文地址:https://www.cnblogs.com/aduner/p/12203626.html
Copyright © 2011-2022 走看看