协程(Coroutine),又称微线程。
我们平常使用的函数又称子程序,是层级调用的,即A函数调用B,B函数又调用C,那么需要等C执行完毕返回,然后B程序执行完毕返回,最后A执行完毕。
协程看上去也是子程序,但是执行顺序和子程序不同:协程执行过程可以中断,同样是A函数调用B,但B可以执行一部分继续去执行A,然后继续执行B未执行完的部分。
协程看起来很像多线程。但协程最大的优势是极高的执行效率。因为线程需要互相切换,切换需要开销。且线程直接共享变量需要使用锁机制,因为协程只有一个线程,不存在同时写变量冲突。
Python对协程的支持是通过generator实现的。
在generator中,我们不但可以通过for循环来迭代,还可以不断调用函数获取由yield
语句返回的下一个值。
但是Python的yield
不但可以返回一个值,它还可以接收调用者发出的参数。下面是一个典型的生产者-消费者
模型:
输出:
[Produce] Start produce 1
[Consumer] Consuming 1
[Produce] Consumer return 200 OK
[Produce] Start produce 2
[Consumer] Consuming 2
[Produce] Consumer return 200 OK
[Produce] Start produce 3
[Consumer] Consuming 3
[Produce] Consumer return 200 OK
[Produce] Start produce 4
[Consumer] Consuming 4
[Produce] Consumer return 200 OK
[Produce] Start produce 5
[Consumer] Consuming 5
[Produce] Consumer return 200 OK
大家可以使用 Intellij IDEA调试功能 进行单步运行观察执行流程。
整个流程由一个线程执行,produce和consumer协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
asyncio
是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。使用asyncio
可以实现单线程并发IO操作。
@asyncio.coroutine
把一个generator标记为coroutine类型:
# coding: utf-8
import asyncio
@asyncio.coroutine
def helloWorld(n):
print('Hello world! %s' % n)
r = yield from asyncio.sleep(3)
loop = asyncio.get_event_loop()
tasks = [helloWorld(1), helloWorld(2), helloWorld(3), helloWorld(4)]
loop.run_until_complete(asyncio.wait(tasks))
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开始引入了新的语法async
和await
,可以让coroutine
的代码更简洁易读。
- 把
@asyncio.coroutine
替换为async
; - 把
yield from
替换为await
。
上节的代码用Python3.5写:
# coding: utf-8
import asyncio
async def helloWorld(n):
print('Hello world! %s' % n)
r = await asyncio.sleep(3)
print('Hello %s %s ' % (r, n))
loop = asyncio.get_event_loop()
tasks = [helloWorld(1), helloWorld(2), helloWorld(3), helloWorld(4)]
loop.run_until_complete(asyncio.wait(tasks))
aiohttp
是基于asyncio
实现的HTTP框架。
需要先安装:
$ pip install aiohttp
控制台输出:
说明安装完成。
示例:
# coding: utf-8
import asyncio
from aiohttp import web
@asyncio.coroutine
def index(request):
@asyncio.coroutine
def user(request):
name = request.match_info['name']
body = 'Hello %s' % name
return web.Response(body=body.encode('utf-8'), content_type='text/html')
pass
@asyncio.coroutine
def init(loop):
# 创建app
app = web.Application(loop=loop)
#添加路由
app.router.add_route('GET', '/', index)
app.router.add_route('GET', '/user/{name}', user)
#运行server
server = yield from loop.create_server(app.make_handler(), '127.0.0.1', 9999)
print('Server is running at http://127.0.0.1:9999 ...')
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()
运行程序:
$ python user_aiohttp.py
浏览器依次输入查看效果:
http://127.0.0.1:9999/user/aiohttp
更多知识可以查看aiohttp文档: