编程随笔

python 协程和异步I/O的实践

2020-03-09  本文已影响0人  jj_jump
Image

python 协程和异步I/O的实践

协程的概念

协程(coroutine)通常又称之为微线程或纤程,它是相互协作的一组子程序(函数)。所谓相互协作指的是在执行函数A时,可以随时中断去执行函数B,然后又中断继续执行函数A。注意,这一过程并不是函数调用(因为没有调用语句),整个过程看似像多线程,然而协程只有一个线程执行。协程通过yield关键字和 send()操作来转移执行权,协程之间不是调用者与被调用者的关系。

协程的优势在于以下两点:

def consumer():
    """
    消费者
    :return:
    """
    count = 0
    while True:
        n = yield count
        if not n:
            return
        print('Consumer %s...' % n)
        count += n


def produce(c):
    """
    生产者
    :param c:
    :return:
    """
    # 激活协程
    c.send(None)
    n = 0
    while n < 5:
        n = n + 1
        print('Produce %s...' % n)
        r = c.send(n)
        print('Consumer return: %s' % r)
    c.close()


if __name__ == '__main__':
    c = consumer()
    produce(c)


注意 : consumer函数是一个generator,把一个consumer传入produce后:

首先调用c.send(None)启动生成器;
然后,一旦生产了东西,通过c.send(n)切换到consumer执行;
consumer通过yield拿到消息,处理,又通过yield把结果传回;
produce拿到consumer处理的结果,继续生产下一条消息;
produce决定不生产了,通过c.close()关闭consumer,整个过程结束。
整个流程无锁,由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

历史回顾

  1. Python 2.2:第一次提出了生成器(最初称之为迭代器)的概念(PEP 255)。
  2. Python 2.5:引入了将对象发送回暂停了的生成器这一特性即生成器的send()方法(PEP 342)。
  3. Python 3.3:添加了yield from特性,允许从迭代器中返回任何值(注意生成器本身也是迭代器),这样我们就可以串联生成器并且重构出更好的生成器。
  4. Python 3.4:引入asyncio.coroutine装饰器用来标记作为协程的函数,协程函数和asyncio及其事件循环一起使用,来实现异步I/O操作。
  5. Python 3.5:引入了async和await,可以使用async def来定义一个协程函数,这个函数中不能包含任何形式的yield语句,但是可以使用return或await从协程中返回值。

异步I/O - 非阻塞式I/O操作。


import asyncio


@asyncio.coroutine
def countdown(name, num):
    while num > 0:
        print(f'Countdown[{name}]: {num}')
        yield from asyncio.sleep(1)
        num -= 1


def main():
    loop = asyncio.get_event_loop()
    tasks = [
        countdown("A", 10), countdown("B", 5),
    ]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()


if __name__ == '__main__':
    main()

import asyncio


async def countdown(name, num):
    while num > 0:
        print(f'Countdown[{name}]: {num}')
        await asyncio.sleep(1)
        num -= 1


def main():
    loop = asyncio.get_event_loop()
    tasks = [
        countdown("A", 10), countdown("B", 5),
    ]
    loop.run_until_complete(asyncio.wait(tasks))
    loop.close()


if __name__ == '__main__':
    main()


参考链接

上一篇 下一篇

猜你喜欢

热点阅读