异步实现的三种方式:
1、callback,callback注册
2、添加到队列
3、placeholder(站位), yield future
协程式的优点:
1、无cpu分时切换线程保存上下文问题(协程上下文怎么保存)
2、遇到io阻塞切换(怎么实现的)
3、无需共享数据的保护锁(为什么)
关键概念:
1、协程式都是由loop调用
2、生成器函数(参数)得到一个生成器对象gen, next(gen)等于gen.send(None),
3、yield(出生成器),gen.send(something)会回到生成器,传递给yield左边,send是生成器独有的
下面用典型的协程逻辑代码分析:
import asyncio
import random
async def smart_fib(n):
index = 0
a = 0
b = 1
while index < n:
sleep_secs = random.uniform(0, 0.2)
await asyncio.sleep(sleep_secs)
print('Smart one think {} secs to get {}'.format(sleep_secs, b))
a, b = b, a + b
index += 1
async def stupid_fib(n):
index = 0
a = 0
b = 1
while index < n:
sleep_secs = random.uniform(0, 0.4)
await asyncio.sleep(sleep_secs)
print('Stupid one think {} secs to get {}'.format(sleep_secs, b))
a, b = b, a + b
index += 1
if __name__ == '__main__':
loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(smart_fib(10)),
asyncio.ensure_future(stupid_fib(10)),
]
loop.run_until_complete(asyncio.wait(tasks))
print('All fib finished.')
loop.close()
宏观上loop和协程的运行过程:
Task是future的子类,增加了对协程函数的调度;协程函数是放在ensure_future or create_task,然后loop.run_until_complete(task),多个任务需要asyncio.wait(tasks)或者asyncio.gather(*tasks)。
A = yield from B(await类似就不讲了),右边B是跟着的coroutine、阻塞IO或者future--send过去的,左边A处理结果 expression或者exception--yield出来,
由此可以看出:yield from约等于yield+异常处理+send
协程微观下运行过程:
关键代码:
@coroutine
def sleep(delay, result=None, *, loop=None):
future = loop.create_future()
h = future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return (yield from future) #这里是疑问的地方
finally:
h.cancel()
阻塞io通过future保存未来的结果,因为future具有没有完成loop可以循环调启future(可以多次send,可以多次yield返回)
class Future:
def __iter__(self):
if not self.done():
self._asyncio_future_blocking = True
yield self # This tells Task to wait for completion.
assert self.done(), "yield from wasn't used with future"
return self.result() # May raise too.
延伸:gevent轮询检测IO阻塞和完成的时候,切换下一个任务;需要定时识别,运行效率没有原生协程高效。
问题: 阻塞IO里面,yield from future到底做了什么,看情况应该是还给loop了而不可能是阻塞这里了,怎样和loop建立的联系