Asyncio 原理分析
异步io的本质是对系统的基本的异步io函数epoll, selector的封装。 但凡异步必将涉及事件-回调,而裸的事件回调会使代码复杂,比如又要考虑出现异常怎么办,回调中套回调, 回调间数据怎么共享,怎么判断谁触发回调都很麻烦。
asyncio这个库就是使用python的yield这个可以打断保存当前函数的上下文的机制, 封装好了selector 摆脱掉了复杂的回调关系
本篇主要是对参考资料那篇文章的一个笔记 ,原文章很精彩,建议大家看一下
原始的selector
class Client:
def __init__(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.resp = b''
def connect(self):
self.sock.setblocking(False)
try:
self.sock.connect(("127.0.0.1", 5003))
print("sock connection")
except BlockingIOError:
pass
selector.register(self.sock.fileno(), selectors.EVENT_WRITE, self.write)
def read(self, key, mask):
chunk = self.sock.recv(4096)
if chunk:
self.resp = self.resp+chunk
else:
selector.unregister(key) # 这个放着里是一次拿4k数据 如果没拿完就一直监听这个事件
print(self.resp)
return
def write(self, key, mask):
print("start write")
selector.unregister(key)
self.sock.send(b"hello")
print("finish write")
selector.register(key, selectors.EVENT_READ, self.read)
if __name__ == '__main__':
c = Client()
c.connect()
while True:
events = selector.select()
for key, mask in events:
callback = key.data
callback(key.fd, mask)
基本思路就是事件-回调的思路, 先register注册一个写事件,等能触发了用unregister标志认领了事件
使用生成器
Future
我们先设计一个Future 表达日后要完成的事情.
class Future:
def __init__(self):
self.result = None
self._callbacks = []
def add_done_callback(self, fn):
self._callbacks.append(fn)
def set_result(self, result):
self.result = result
for fn in self._callbacks:
fn(self)
每次执行set_result会调用call_back
Task
Task是用来驱动future的
class Task:
def __init__(self, coro):
self.coro = coro
f = Future()
f.set_result(None)
self.step(f)
def step(self, future):
try:
next_future = self.coro.send(future.result)
except StopIteration:
return
next_future.add_done_callback(self.step)
Task 要做的事情就是每次让协程接着往下走,并且在不是最后一步的时候,把下一步加到回调里
Main
def connect(self):
self.sock.setblocking(False)
try:
self.sock.connect(('127.0.0.1',30))
except BlockingIOError:
pass
f = Future()
def on_connection():
f.result(None)
selector.register(self.sock.fileno(), selectors.EVENT_WRITE, on_connection)
yield f
selector.unregister(self.sock.fileno())
self.sock.sendall(b"hello")
while True:
f = Future()
def on_read():
f.set_result(self.sock.recv(4096))
selector.register(self.sock.fileno(), selectors.EVENT_READ, on_read)
chunk = yield f
selector.unregister(self.sock.fileno())
if chunk:
self.resp += chunk
else:
break
这里可以看到 所有的回调都加上了 set_result 方法 通过set_result 来调用绑定在future的回调,主要就是Task给 绑定的下一步的回调。
这样的好处就是不用区分事件是谁触发的了 每次都按的是同步的顺序走的。
Eventloop
c = Client()
Task(c.connect())
while True:
events = selector.select()
for key, mask in events:
callback = key.data
callback()
最后写出Eventloop是一件十分自然的事情, 先把任务加到Task里预激励它。然后每次触发事件就调用set_result方法来调用Task给他 附加的step的回调,让协程一直往下走。这样就用可以让代码保持了同步的顺序又能异步执行了。
参考资料