协程

通常在python中设计并发编程都是使用多进程和多线程来实现的,由于GIL锁的的存在对于CPU密集型的任务通常使用多进程来实现,而对于IO密集型任务通常采用多线程来实现,让线程在调度的时候让出GIL,从而在表面上实现并发。其实对于IO密集型任务我们还可以利用协程来实现。

协程

协程,被称为微线程。协程是运行在单线程中的并发,通过程序的切换省去了线程之间切换的开销,提高了效率。asyncio异步模块就是协程模块。协程也不需要锁的机制因为协程是在同一个线程之中切换的,避免了同时访问数据的问题,依次执行效率要高得多。通过多进程+协程的方式来利用多核CPU以此提高性能。

协程实现的生产者消费者模型。

import random
import time

def producer(consumer):
    next(consumer)
    while True:
        data=random.randint(0,99)
        print("生产者生产了:",data)
        consumer.send(data)
        time.sleep(1)
def consumer():
    while True:
        item=yield
        print("消费者消费了:",item)

if __name__ == '__main__':
    c=consumer()
    p=producer(c)

yield

yield关键字在python中用来制造生成器,也就是说带有yield关键字的函数就是生成器函数,yield执行时会在yield处停住将yield后面的表达式返回(默认None),直到被next()方法调用才会继续执行。当没有办法yield时就会抛出异常。除了用next()语法,for循环遍历也是可以取到所有值。具体内容可参考以前的文章。生成器。

send

send的语法可以让yield接收外部发来的值,这样一个生成器就变成了协程。每个生成器都可以执行send()方法为yield 发送数据,此时yield不但可以暂停并返回函数,还可以接受send传过来的值并重新激活函数,将值赋给一个变量x(x = yield xxxxx)。

def func():
    i=0
    while True:
       x=yield i
       i+=1
       print("第{}次执行fun函数,x的值时{}".format(i,x))
g=func()
next(g)
g.send("hello")
g.send("python")


>>>>第1次执行fun函数,x的值时hello
    第2次执行fun函数,x的值时python

协程可以处于下面四个状态中的一个。当前状态可以导入inspect模块,使用inspect.getgeneratorstate(...) 方法查看,该方法会返回下述字符串中的一个。

  • 'GEN_CREATED'  等待开始执行。

  • 'GEN_RUNNING'  协程正在执行。

  • 'GEN_SUSPENDED' 在yield表达式处暂停。

  • 'GEN_CLOSED'   执行结束。

@asyncio.coroutine和yield from

在python3.4开始asyncio加入python的标准库中。可以通过此来轻松的完成协程实现的一部IO操作。asyncio是一个基于事件循环的异步IO模块,通过yield from,我们可以将协程asyncio.sleep()的控制权交给事件循环,然后挂起当前协程;之后,由事件循环决定何时唤醒asyncio.sleep,接着向后执行代码。@asyncio.coroutine用于将函数声明为协程。yield from就是等待另一个函数的返回。

 

import asyncio
import datetime

@asyncio.coroutine  # 声明一个协程
def display_date(num, loop):
    while True:
        print("任务{}完成,time:{}".format(num,datetime.datetime.now()))
        yield from asyncio.sleep(2)  # 阻塞直到协程sleep(2)返回结果
loop = asyncio.get_event_loop()  # 获取一个event_loop
tasks = [display_date(1, loop), display_date(2, loop)]
loop.run_until_complete(asyncio.gather(*tasks))  # "阻塞"直到所有的tasks完成
loop.close()

>>>
任务1完成,time:2018-09-23 14:16:12.091581
任务2完成,time:2018-09-23 14:16:12.092156
任务1完成,time:2018-09-23 14:16:14.093619
任务2完成,time:2018-09-23 14:16:14.094123
任务1完成,time:2018-09-23 14:16:16.097316
任务2完成,time:2018-09-23 14:16:16.097562
任务1完成,time:2018-09-23 14:16:18.099375
任务2完成,time:2018-09-23 14:16:18.100089

 

async、await

在python3.5中,引入async和await 关键字。让协程独立于生成器而存在。

import asyncio
import datetime

# @asyncio.coroutine  # 声明一个协程
async def display_date(num, loop):
    while True:
        print("任务{}完成,time:{}".format(num,datetime.datetime.now()))
        # yield from asyncio.sleep(2)  # 阻塞直到协程sleep(2)返回结果
        await asyncio.sleep(2)
loop = asyncio.get_event_loop()  # 获取一个event_loop
tasks = [display_date(1, loop), display_date(2, loop)]
loop.run_until_complete(asyncio.gather(*tasks))  # "阻塞"直到所有的tasks完成
loop.close()

asyncio模块

asyncio的使用主要分为创建事件循环、指定循环模式并运行、关闭循环。

通常使用asyncio.get_event_loop()创建事件循环。运行循环有两种方法:一是调用run_until_complete()方法,二是调用run_forever()方法。前者内置add_done_callback 回调函数,后者则可以自定义此函数。

import asyncio
async def func(future):
    await asyncio.sleep(1)
    future.set_result("执行!")
if __name__ == '__main__':
    loop=asyncio.get_event_loop()
    future=asyncio.Future()
    asyncio.ensure_future(func(future))
    print(loop.is_running())
    loop.run_until_complete(future)
    print(future.result())
    loop.close()
import asyncio
async def func(future):
    await asyncio.sleep(1)
    future.set_result("执行!")
def call_result(future):
    print(future.result())
    loop.stop()
if __name__ == '__main__':
    loop=asyncio.get_event_loop()
    loop = asyncio.get_event_loop()
    future = asyncio.Future()
    asyncio.ensure_future(func(future))
    future.add_done_callback(call_result)
    try:
        loop.run_forever()
    finally:
        loop.close()

greenlet模块

greenlet为第三方库,使用前先安装pip install greenlet。

这个库为以前的python中,当然python3中提供了前面所讲的更好的方法。该模块也可以让函数变成协程。

from greenlet import greenlet
import random
import time
def producer():
    while True:
        data=random.randint(0,99)
        print("生产者生产了",data)
        c.switch(data)
        time.sleep(1)

def consumer():
    while True:
        item=p.switch()
        print("消费者消费了",item)

p=greenlet(producer)
c=greenlet(consumer)
c.switch()

 gevent

gevent封装了libev(基于epoll)和greenlet两个库。遇到阻塞就会切换。既没有轮询的开销也没有线程的开销。

import time
import random
import gevent
from gevent.queue import Queue
queue=Queue(2)
def producer(queue,):
    while True:
        data=random.randint(0,99)
        print('生产者生产了:',data)
        queue.put(data)
        time.sleep(1)
def consumer(queue):
    while True:
        data=queue.get()
        print("消费者消费了:",data)
if __name__ == '__main__':

    p=gevent.spawn(producer,queue)
    c = gevent.spawn(consumer, queue)
    gevent.joinall([p,c])

 

posted @ 2018-09-23 15:42  AustinJoe  阅读(176)  评论(0编辑  收藏  举报