协程(Coroutine),又称微线程。

    我们平常使用的函数又称子程序,是层级调用的,即A函数调用B,B函数又调用C,那么需要等C执行完毕返回,然后B程序执行完毕返回,最后A执行完毕。

    协程看上去也是子程序,但是执行顺序和子程序不同:协程执行过程可以中断,同样是A函数调用B,但B可以执行一部分继续去执行A,然后继续执行B未执行完的部分。

    协程看起来很像多线程。但协程最大的优势是极高的执行效率。因为线程需要互相切换,切换需要开销。且线程直接共享变量需要使用锁机制,因为协程只有一个线程,不存在同时写变量冲突。

    Python对协程的支持是通过generator实现的。

    在generator中,我们不但可以通过for循环来迭代,还可以不断调用函数获取由yield语句返回的下一个值。

    但是Python的yield不但可以返回一个值,它还可以接收调用者发出的参数。下面是一个典型的生产者-消费者模型:

    输出:

    1. [Produce] Start produce 1
    2. [Consumer] Consuming 1
    3. [Produce] Consumer return 200 OK
    4. [Produce] Start produce 2
    5. [Consumer] Consuming 2
    6. [Produce] Consumer return 200 OK
    7. [Produce] Start produce 3
    8. [Consumer] Consuming 3
    9. [Produce] Consumer return 200 OK
    10. [Produce] Start produce 4
    11. [Consumer] Consuming 4
    12. [Produce] Consumer return 200 OK
    13. [Produce] Start produce 5
    14. [Consumer] Consuming 5
    15. [Produce] Consumer return 200 OK

    大家可以使用 Intellij IDEA调试功能 进行单步运行观察执行流程。

    整个流程由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

    asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。使用asyncio可以实现单线程并发IO操作。

    @asyncio.coroutine把一个generator标记为coroutine类型:

    1. # coding: utf-8
    2. import asyncio
    3. @asyncio.coroutine
    4. def helloWorld(n):
    5. print('Hello world! %s' % n)
    6. r = yield from asyncio.sleep(3)
    7. loop = asyncio.get_event_loop()
    8. tasks = [helloWorld(1), helloWorld(2), helloWorld(3), helloWorld(4)]
    9. loop.run_until_complete(asyncio.wait(tasks))
    10. loop.close()

    输出:

    程序先运行Hello world! ,然后由于也是一个coroutine,线程不会等待asyncio.sleep(),而是直接中断并执行下一个消息循环。当asyncio.sleep()返回时,线程就可以从yield from拿到返回值(此处是None),然后接着执行下一行语句。

    用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异步操作。

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

    1. @asyncio.coroutine替换为async
    2. yield from替换为await

    上节的代码用Python3.5写:

    1. # coding: utf-8
    2. import asyncio
    3. async def helloWorld(n):
    4. print('Hello world! %s' % n)
    5. r = await asyncio.sleep(3)
    6. print('Hello %s %s ' % (r, n))
    7. loop = asyncio.get_event_loop()
    8. tasks = [helloWorld(1), helloWorld(2), helloWorld(3), helloWorld(4)]
    9. loop.run_until_complete(asyncio.wait(tasks))

    aiohttp是基于asyncio实现的HTTP框架。

    需要先安装:

    1. $ pip install aiohttp

    控制台输出:

    说明安装完成。

    示例:

    1. # coding: utf-8
    2. import asyncio
    3. from aiohttp import web
    4. @asyncio.coroutine
    5. def index(request):
    6. @asyncio.coroutine
    7. def user(request):
    8. name = request.match_info['name']
    9. body = 'Hello %s' % name
    10. return web.Response(body=body.encode('utf-8'), content_type='text/html')
    11. pass
    12. @asyncio.coroutine
    13. def init(loop):
    14. # 创建app
    15. app = web.Application(loop=loop)
    16. #添加路由
    17. app.router.add_route('GET', '/', index)
    18. app.router.add_route('GET', '/user/{name}', user)
    19. #运行server
    20. server = yield from loop.create_server(app.make_handler(), '127.0.0.1', 9999)
    21. print('Server is running at http://127.0.0.1:9999 ...')
    22. loop = asyncio.get_event_loop()
    23. loop.run_until_complete(init(loop))
    24. loop.run_forever()

    运行程序:

    1. $ python user_aiohttp.py

    浏览器依次输入查看效果:

    http://127.0.0.1:9999/user/aiohttp

    更多知识可以查看aiohttp文档: