参考:
深入理解Python异步编程
理解 Python asyncio
(1)异步非租塞
http://blog.csdn.net/yz764127031/article/details/72459158
以春运火车票为例,
异步—-你使用分流软件抢票,设置好时间类型,软件自动运行
非阻塞—-你不用等抢票结果,可以去做别的事
如何获得结果:
轮询—–每隔一段时间,你看一下抢到票没有
事件通知—-设置抢到票,邮件或者短信通知
为什么这么做?
假如你是一个黄牛,你可以通过这种方法抢到成千上万的票,也就是提高并发量
(2)Python中的异步与协程
假如你是黄牛,
软件自动运行之后,你要记住是这是哪个客户的票—-保存上下文
买到票后,你要去告诉相应的客户,去支付费用——恢复上下文
Python中对应的就是:
保存协程状态
生成器yield
恢复协程状态
send
事件通知
Linux系统提供的epoll
(3)例子
来源:https://github.com/denglj/aiotutorial/blob/master/part_1/generator.py
(1)注册事件和保存上下文
class Crawler:
def __init__(self, url):
self.url = url
self.response = b''
def fetch(self):
sock = socket.socket()
sock.setblocking(False)
try:
sock.connect(('example.com', 80))
except BlockingIOError:
pass
f = Future()
def on_connected():
f.set_result(None)
selector.register(sock.fileno(), EVENT_WRITE, on_connected)
yield f #注册事件和保存上下文
selector.unregister(sock.fileno())
get = 'GET {0} HTTP/1.0\r\nHost: example.com\r\n\r\n'.format(self.url)
sock.send(get.encode('ascii'))
global stopped
while True:
f = Future()
def on_readable():
f.set_result(sock.recv(4096))
selector.register(sock.fileno(), EVENT_READ, on_readable)
chunk = yield f #注册事件和保存上下文
selector.unregister(sock.fileno())
if chunk:
self.response += chunk
else:
urls_todo.remove(self.url)
if not urls_todo:
stopped = True
break
(2)恢复协程状态
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)
class Task:
def __init__(self, coro):
self.coro = coro
f = Future()
f.set_result(None)
self.step(f)
def step(self, future):
try:
# send会进入到coro执行, 即fetch, 直到下次yield
# next_future 为yield返回的对象
next_future = self.coro.send(future.result)#恢复状态
except StopIteration:
return
next_future.add_done_callback(self.step)
(3)事件循环
def loop():
while not stopped:
# 阻塞, 直到一个事件发生
events = selector.select()
for event_key, event_mask in events:
callback = event_key.data
callback()