协程(Coroutine),又称微线程。
我们平常使用的函数又称子程序,是层级调用的,即 A 函数调用 B,B 函数又调用 C,那么需要等 C 执行完毕返回,然后 B 程序执行完毕返回,最后 A 执行完毕。
协程看上去也是子程序,但是执行顺序和子程序不同:协程执行过程可以中断,同样是 A 函数调用 B,但 B 可以执行一部分继续去执行 A,然后继续执行 B 未执行完的部分。
协程看起来很像多线程。但协程最大的优势是极高的执行效率。因为线程需要互相切换,切换需要开销。且线程直接共享变量需要使用锁机制,因为协程只有一个线程,不存在同时写变量冲突。
Python 对协程的支持是通过 generator 实现的。
在 generator 中,我们不但可以通过 for 循环来迭代,还可以不断调用
函数获取由
- next()
语句返回的下一个值。
- yield
但是 Python 的
不但可以返回一个值,它还可以接收调用者发出的参数。下面是一个典型的
- yield
模型:
- 生产者-消费者
- # coding: utf-8
- def consumer():
- r = ''
- while True:
- n = yield r
- print('[Consumer] Consuming %s' % n)
- r = '200 OK'
- def produce(c):
- c.send(None) #启动生成器
- i = 0
- while i < 5:
- i = i + 1
- print('[Produce] Start produce %s' % i)
- r = c.send(i)
- print('[Produce] Consumer return %s' % r)
- c = consumer() #生成器
- produce(c)
输出:
- [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
大家可以使用 进行单步运行观察执行流程。
整个流程由一个线程执行,produce 和 consumer 协作完成任务,所以称为 "协程",而非线程的抢占式多任务。
是 Python 3.4 版本引入的标准库,直接内置了对异步 IO 的支持。使用
- asyncio
可以实现单线程并发 IO 操作。
- asyncio
把一个 generator 标记为 coroutine 类型:
- @asyncio.coroutine
- # coding: utf-8
- import asyncio
- @asyncio.coroutine
- def helloWorld(n):
- print('Hello world! %s' % n)
- r = yield from 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))
- loop.close()
输出:
- Hello world! 2
- Hello world! 3
- Hello world! 1
- Hello world! 4
- #(等待3秒左右)
- Hello None 2
- Hello None 1
- Hello None 3
- Hello None 4
程序先运行
,然后由于
- Hello world!
也是一个 coroutine,线程不会等待
- asyncio.sleep()
,而是直接中断并执行下一个消息循环。当
- asyncio.sleep()
返回时,线程就可以从
- asyncio.sleep()
拿到返回值(此处是
- yield from
),然后接着执行下一行语句。
- None
用 asyncio 提供的
可以把一个
- @asyncio.coroutine
标记为
- generator
类型,然后在
- coroutine
内部用
- coroutine
调用另一个
- yield from
实现异步操作。
- coroutine
为了简化并更好地标识异步 IO,从 Python 3.5 开始引入了新的语法
和
- async
,可以让
- await
的代码更简洁易读。
- coroutine
请注意,
和
- 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))
- loop.close()
是基于
- aiohttp
实现的 HTTP 框架。
- asyncio
需要先安装:
- $ pip install aiohttp
控制台输出:
- Collecting aiohttp
- Downloading aiohttp-1.3.1-cp34-cp34m-win32.whl (147kB)
- 100% |████████████████████████████████| 153kB 820kB/s
- Collecting async-timeout>=1.1.0 (from aiohttp)
- Downloading async_timeout-1.1.0-py3-none-any.whl
- Collecting yarl>=0.8.1 (from aiohttp)
- Downloading yarl-0.9.6-cp34-cp34m-win32.whl (77kB)
- 100% |████████████████████████████████| 81kB 1.8MB/s
- Collecting chardet (from aiohttp)
- Downloading chardet-2.3.0-py2.py3-none-any.whl (180kB)
- 100% |████████████████████████████████| 184kB 890kB/s
- Collecting multidict>=2.1.4 (from aiohttp)
- Downloading multidict-2.1.4-cp34-cp34m-win32.whl (133kB)
- 100% |████████████████████████████████| 143kB 3.3MB/s
- Installing collected packages: async-timeout, multidict, yarl, chardet, aiohttp
- Successfully installed aiohttp-1.3.1 async-timeout-1.1.0 chardet-2.3.0 multidict-2.1.4 yarl-0.9.6
说明安装完成。
示例:
- # coding: utf-8
- import asyncio
- from aiohttp import web
- @asyncio.coroutine
- def index(request):
- return web.Response(body=b'Hello aiohttp!', content_type='text/html')
- @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
- Server is running at http://127.0.0.1:9999 ...
浏览器依次输入查看效果:
更多知识可以查看 aiohttp 文档:
来源: http://www.cnblogs.com/52fhy/p/6407121.html