协程定义
协程的概念早期是由生成器yield -->yield from-->coroutine 演变而来的,熟悉yield关键字都知道一个重要的性质:会从程序上次中断的地方继续往下执行-->有自己的上下文寄存器和栈,不被内核控制,由开发者决定什么时候让出执行权。 现在有很多概念说是一种轻量级的线程(可能大概是因为协程在线程程中被创建)...
个人理解:协程是一种用户态线程,切换不由内核而由用户自定义的线程.
为什么有协程
比较一下协程相对线程的优势|
种类 | 线程 | 协程 |
---|---|---|
上下文切换开销 | 大 | 小 |
并发能力 | 弱 | 强 |
资源同步考虑 | 考虑 | 不考虑 |
协程概念组成
1. 事件循环:所有的协程对象会被注册到该循环中
loop = asyncio.get_event_loop()
2. 协程对象:常见IO操作封装在协程对象中,由关键字async 定义
async def request():
url = 'http://127.0.0.1:5000'
print('Waiting for', url)
result = await get(url)
print('Get response from', url, 'Result:', result)
3.任务(期物):对协程对象的进一步封装,比如会赋予协程状态
task = asyncio.ensure_future(request())
4.await关键字 (区别于asyncio.wait(fps,timeout=)):前者是关键字。后者解决事件循环中某一协程阻塞时间过程的问题
协程的简单使用例子
from flask import Flask
import time
app = Flask(__name__)
@app.route('/')
def index():
time.sleep(3)
return 'hello'
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, threaded=True)
使用流程 创建task(s),创建事件循环,将任务提交到事件循环中去:
# tasks = [asyncio.ensure_future(get_files(i)) for i in range(6)]
#创建任务列表(里面包含协程对象) 但是这并不是一个协程对象
tasks = [asyncio.ensure_future(request()) for _ in range(4)]
#开启事件循环
loop = asyncio.get_event_loop()
#将任务列表添加到事件里面(若是协程对象 直接传协程对象)
loop.run_until_complete(asyncio.wait(tasks))
# p = ThreadPoolExecutor(max_workers=120)
# for _ in range(100):
# p.submit(tes)
# p.shutdown(wait=True)
end = time.time()
print('Cost time:', end - start)
![]
这样的一个程序确实 是起到了协程的作用,遇到await关键字,将程序挂起,执行权让给其他协程,问题是在于请求网络阻塞的时间太长,而使得我们希望的高并发能力表现不出来,最终导致的是
13s的时间,在理论上如果我们能够将请求网络这个操作挂起的话,就真正的能够实现我们所希望的并发能力,但是实际是 请求网络返回的Response类并没有实现__await__方法,所以只能生生阻塞成线性时间
而这个时候 解决问题的库 aiohttp 就出来了,异步库:解决问题的核心是什么呢? ---->背后往往都会借助于IO的事件循环B来实现异步,我们请求网络遭遇阻塞时,将该套接字放入另外一个事件循环B里面,一直维持连接监听,而在主线程的事件循环A中 继续执行其他协程,当另外一个IO事件循环B里面的监听状态发生改变,会唤醒当前的事件循环A中的协程,进行我们需要的处理,这样!我们所期望的并发能力就出现了
我也有用concurrent.future 里面的线程池 实现过100个线程异步提交和100个协程的比较 结果 前者是3.5S后者还是2.1左右 是真的猛啊!
协程进一步了解
0.协程的前世 yield 和 yield from是怎么实现原生协程的
1.如何动态得往事件中添加任务
2.如何查看事件循环中的各任务状态
3.如何开启一个新的事件循环
4.协程相关的异步库
5.tornado里面的异步是怎么实现的...
有错请指出哈........................