首先簡單介紹幾個概念:
進行對比測試之前,我們先來創建一個合適的實驗環境:
模擬一個需要等待一定時間纔可以獲取返回結果的網頁。
如果直接用百度、CSDN 等站點的話,一方面響應太快、難以看出各種方法的差距,另一方面響應速度會受網速影響、每次發送請求獲取響應所需的時間不完全一致導致重複實驗結果差距較大,所以在此用 Flask 模擬一個本地慢速服務器。
flask_server.py
代碼如下:
from flask import Flask # pip install flask import time app = Flask(__name__) @app.route('/') def index(): time.sleep(3) # 休眠 3 秒再返回結果 return 'Hello!' if __name__ == '__main__': app.run(threaded=True) # 以多線程模式啓動服務
啓動之後,Flask 服務默認在 127.0.0.1:5000
上運行,控制檯輸出結果如下:
* Serving Flask app "flask_server" (lazy loading) * Environment: production WARNING: Do not use the development server in a production environment. Use a production WSGI server instead. * Debug mode: off * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
在瀏覽器中訪問 http://127.0.0.1:5000/
等待 3 秒即會出現 Hello! 頁面,表明簡單的慢速服務器搭建完成!
首先導入需要的模塊,以請求 10 次爲例準備 urls
,再定義一個 get_html_text()
函數:
import requests import time # 用於多進程 from multiprocessing import Process # 用於多線程 from threading import Thread # 用於協程+異步 import asyncio import aiohttp # pip install aiohttp urls = ['http://127.0.0.1:5000' for _ in range(10)] def get_html_text(url): response = requests.get(url) return response.text
start = time.time() for url in urls: result = get_html_text(url) print(result) end = time.time() print('【單進程單線程】耗時:%s 秒' %(end - start))
結果如下:
Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! 【單進程單線程】耗時:30.15605854988098 秒
start = time.time() processes = [] for url in urls: p = Process(target=get_html_text, args=(url,)) processes.append(p) p.start() for p in processes: p.join() print('Hello!') end = time.time() print('【多進程 並行】耗時:%s 秒' %(end - start))
結果如下(測試電腦爲 4 核 CPU,核心數越大加速越明顯):
Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! 【多進程 並行】耗時:5.511234283447266 秒
start = time.time() threads = [] for url in urls: t = Thread(target=get_html_text, args=(url,)) threads.append(t) t.start() for t in threads: t.join() print('Hello!') end = time.time() print('【多線程 併發】耗時:%s 秒' %(end - start))
結果如下:
Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! 【多線程 併發】耗時:3.030653953552246 秒
# 因爲 requests 模塊不支持異步操作,需要藉助 aiohttp 模塊 async def get_html_text_async(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: text = await response.text() return text start = time.time() tasks = [asyncio.ensure_future(get_html_text_async(url)) for url in urls] loop = asyncio.get_event_loop() loop.run_until_complete(asyncio.wait(tasks)) for task in tasks: print(task.result()) end = time.time() print('【協程 ++ 異步】耗時:%s 秒' %(end - start))
結果如下:
Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! Hello! 【協程 ++ 異步】耗時:3.046288251876831 秒
len(urls)==1:
len(urls)==4:
len(urls)==10:
len(urls)==100:
3n+
秒;time.sleep(3)
)時被掛起,n 個任務都處於掛起狀態後等待 3 秒,n 個請求幾乎同時都有了響應,然後掛起的任務被喚醒接着執行,輸出請求結果,最後耗時:3 秒!(多出來的時間是 IO 時延)注意:
- 搭建的實驗環境是慢速服務器,若不進行 time.sleep(3) 休眠 3 秒再返回 而是立即響應的話,單進程單線程的實際耗時則會大大縮短,請求次數少的話甚至會超過多進程。
- 而且筆者在 Windows 4 核 CPU 環境下測試,最多開啓 4 個進程,未能發揮多進程的真實實力。
- 另外,多進程、多線程還可以通過進程池、線程池來實現,與文中方法耗時基本一致,故未做展示;多進程或多線程與協程異步IO結合的效率尚待測試。