異步IO的出現源自於CPU速度與IO速度徹底不匹配python
通常的能夠採用多線程或者多進程的方式來解決IO等待的問題web
一樣異步IO也能夠解決同步IO所帶來的問題服務器
常見的異步IO的實現方式是使用一個消息循環, 主線程不斷的讀取這個消息循環以便肯定IO操做是否完成多線程
協程(微線程, 纖程)併發
通常子程序調用是一個入口一個出口, 調用的順序也是明確的app
可是協程不一樣, 執行過程當中子程序內部中斷, 就會轉而執行別的子程序, 等待合適的時間返回 , 這樣的行爲相似於進程的切換 webapp
正由於協程又相似於線程執行的特性, 可是子程序切換幾乎沒有切換開銷, 所以性能很好異步
並且協程還能夠避免臨界資源操做的問題async
協程是一個線程執行函數
構造協程通常是使用生成器, 利用send()發送相互傳遞數據
常見的生產者消費者的範例
def consumer():
r = ''
while True:
n = yield r
if not n:
return
print('[CONSUMER] Consuming %s...' % n)
r = '200 OK'
def produce(c):
c.send(None)
n = 0
while n < 5:
n = n + 1
print('[PRODUCER] Producing %s...' % n)
r = c.send(n)
print('[PRODUCER] Consumer return: %s' % r)
c.close()
c = consumer()
produce(c)
基本使用爲
import asyncio
@asyncio.coroutine
def hello():
print("Hello world!")
# 異步調用asyncio.sleep(1):
r = yield from asyncio.sleep(1)
print("Hello again!")
# 獲取EventLoop:
loop = asyncio.get_event_loop()
# 執行coroutine
loop.run_until_complete(hello())
loop.close()
使用裝飾器asyncio.coroutine裝飾一個執行函數, 這樣即可以生成一個異步執行IO的函數
使用asyncio.get_event_loop()建立一個循環
經過這個循環的對象執行run_until_complete()來執行異步IO
其中run_until_complete()能夠傳入一個函數執行
若是須要多個函數的話, 就須要將這些函數執行方法一個list中, 並使用asyncid.wait(list)
tasks = [hello(), hello()]
loop.run_until_complete(asyncio.wait(tasks))
最後調用close()結束異步IO
其中函數的寫法是 須要使用 yield from來實現異步IO
獲取網頁的函數編寫以下
@asyncio.coroutine
def wget(host):
print('wget %s...' % host)
connect = asyncio.open_connection(host, 80)
reader, writer = yield from connect
header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
writer.write(header.encode('utf-8'))
yield from writer.drain()
while True:
line = yield from reader.readline()
if line == b'\r\n':
break
print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
writer.close()
使用asyncio.coroutine能夠吧一個生成器標記爲coroutine類型
而後在內部使用yield from調用另外一個coroutine實現異步操做
爲了進一步簡化操做, 出現了async和await, 這是在python3.5開始支持的語法
簡化以下
1) 裝飾器asyncio.corontine替換爲async
2) yield from替換爲await
所以上述代碼能夠簡化爲
async def hello():
print("Hello world!")
r = await asyncio.sleep(1)
print("Hello again!")
async def wget(host):
print('wget %s...' % host)
connect = asyncio.open_connection(host, 80)
reader, writer = await connect
header = 'GET / HTTP/1.0\r\nHost: %s\r\n\r\n' % host
writer.write(header.encode('utf-8'))
await writer.drain()
while True:
line = await reader.readline()
if line == b'\r\n':
break
print('%s header > %s' % (host, line.decode('utf-8').rstrip()))
writer.close()
asyncio實現了單線程併發io操做, 若是隻是應用於客戶端, 那麼做用還不是那麼明顯
針對於服務器, asyncio可能發揮更好的效果, 基於http協議實現的asyncio就是aiohttp
安裝
pip install aiohttp
1) 導入包
import asyncio
from aiohttp import web
2) 實現mian代碼
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()
3) 編寫初始化函數
將循環loop做爲參數傳入, 用於建立webapp
添加路由, 並指定處理函數
最後建立服務器, 利用create_server()建立TCP服務
async def init(loop):
app = web.Application(loop=loop)
app.router.add_route('GET', '/', index)
app.router.add_route('GET', '/hello/{name}', hello)
srv = await loop.create_server(app.make_handler(), '127.0.0.1', 8000)
print('Server started at http://127.0.0.1:8000...')
return srv
4) 編寫兩個路由的處理函數
async def index(request):
await asyncio.sleep(0.5)
return web.Response(body=b'<h1>Index</h1>')
async def hello(request):
await asyncio.sleep(0.5)
text = '<h1>hello, %s!</h1>' % request.match_info['name']
return web.Response(body=text.encode('utf-8'))