python 一直在進行併發編程的優化, 比較熟知的是使用 thread 模塊多線程和 multiprocessing 多進程,後來慢慢引入基於 yield 關鍵字的協程。 而近幾個版本,python 對於協程的寫法進行了大幅的優化,不少以前的協程寫法不被官方推薦了。若是你以前瞭解過 python 協程,你應該看看最新的用法。html
併發、並行、同步和異步
併發指的是 一個 CPU 同時處理多個程序,可是在同一時間點只會處理其中一個。併發的核心是:程序切換。python
可是由於程序切換的速度很是快,1 秒鐘內能夠徹底不少次程序切換,肉眼沒法感知。
git
並行指的是多個 CPU 同時處理多個程序,同一時間點能夠處理多個。
github
同步:執行 IO 操做時,必須等待執行完成才獲得返回結果。
異步:執行 IO 操做時,沒必要等待執行就能獲得返回結果。
web
協程,線程和進程的區別
多進程一般利用的是多核 CPU 的優點,同時執行多個計算任務。每一個進程有本身獨立的內存管理,因此不一樣進程之間要進行數據通訊比較麻煩。數據庫
多線程是在一個 cpu 上建立多個子任務,當某一個子任務休息的時候其餘任務接着執行。多線程的控制是由 python 本身控制的。 子線程之間的內存是共享的,並不須要額外的數據通訊機制。可是線程存在數據同步問題,因此要有鎖機制。編程
協程的實現是在一個線程內實現的,至關於流水線做業。因爲線程切換的消耗比較大,因此對於併發編程,能夠優先使用協程。api
。。。
這是對比圖:
網絡
協程的基礎使用
這是 python 3.7 裏面的基礎協程用法,如今這種用法已經基本穩定,不太建議使用以前的語法了。多線程
import asyncio import time async def visit_url(url, response_time): """訪問 url""" await asyncio.sleep(response_time) return f"訪問{url}, 已獲得返回結果" start_time = time.perf_counter() task = visit_url('http://wangzhen.com', 2) asyncio.run(task) print(f"消耗時間:{time.perf_counter() - start_time}")
- 1, 在普通的函數前面加 async 關鍵字;
- 2,await 表示在這個地方等待子函數執行完成,再往下執行。(在併發操做中,把程序控制權教給主程序,讓他分配其餘協程執行。) await 只能在帶有 async 關鍵字的函數中運行。
- 3, asynico.run() 運行程序
- 4, 這個程序消耗時間 2s 左右。
增長協程
再添加一個任務:
task2 = visit_url('http://another.com', 3) asynicio.run(task2)
這 2 個程序一共消耗 5s 左右的時間。並無發揮併發編程的優點。若是是併發編程,這個程序只須要消耗 3s,也就是task2的等待時間。要想使用併發編程形式,須要把上面的代碼改一下。
import asyncio import time async def visit_url(url, response_time): """訪問 url""" await asyncio.sleep(response_time) return f"訪問{url}, 已獲得返回結果" async def run_task(): """收集子任務""" task = visit_url('http://wangzhen.com', 2) task_2 = visit_url('http://another', 3) await asyncio.run(task) await asyncio.run(task_2) asyncio.run(run_task()) print(f"消耗時間:{time.perf_counter() - start_time}")
asyncio.gather 會建立 2 個子任務,當出現 await 的時候,程序會在這 2 個子任務之間進行調度。
create_task
建立子任務除了能夠用 gather 方法以外,還能夠使用 asyncio.create_task 進行建立。
async def run_task(): coro = visit_url('http://wangzhen.com', 2) coro_2 = visit_url('http://another.com', 3) task1 = asyncio.create_task(coro) task2 = asyncio.create_task(coro_2) await task1 await task2
協程的主要使用場景
協程的主要應用場景是 IO 密集型任務,總結幾個常見的使用場景:
- 網絡請求,好比爬蟲,大量使用 aiohttp
- 文件讀取, aiofile
- web 框架, aiohttp, fastapi
- 數據庫查詢, asyncpg, databases
進一步學習方向(接下來的文章)
- 何時用協程,何時用多線程,何時用多進程
- future 對象
- asyncio 的底層 api
- loop
- trio 第三方庫用法