當我們處理 IO 密集型的任務的時候很容易相當多線程。
Python 因為 GIL 的關系我們沒有辦法在 CPU 密集型任務的情況下讓解釋器讓出 CPU,但是當面對網絡請求相關的任務的時候,我們卻可以使用多線程切換來幫助我們節約 IO 等待時間。
Python3 之后我發現很多之前的惡心的寫法現在都變得異常好用,比如要實現一個多線程的程序,現在也變得簡單易懂。
只需要一個簡單的上下文管理器
with ThreadPoolExecutor(self.thread_concurrence) as executor: fs = [executor.submit(self._orm_iter_insert_bigquery, table_name, start, end) for start, end in start_end_list] for future in as_completed(fs): print(future.result())
就可以讓我們的程序多線程運行。
executor.submit 可以 add 一個任務到多線程列表。
這里我們使用一個循環提交一些列任務到任務列表。注意 thread_concurrence 為我們指定的並發數,默認是 cpu_count * 5。
我使用一個 as_completed 來獲取已經完成任務的結果。與它相對應的還有一個 wait 參數,wait 參數可以更精細的控制同步等待,具體的可以參看一下 reference 官方的文檔。
值得注意的是,在寫多線程程序的時候一定要注意自己在類上創建的很多連接是否是 線程安全
舉個最簡單的例子,大部分 Python 的 MySQL 連接都並非是線程安全的,如果多個線程使用同一個連接,你會發現你的多線程程序不起作用。
正確的方法是,如果你不確定你自己發起的客戶端連接是否是線程安全或者可以復用的,最好是將 客戶端的初始化,放在對應的多線程執行函數里面,這樣就可以避免此類的問題。
類如這里就應該將相關連接放進函數 _orm_iter_insert_bigquery 中來避免線程不安全的情況。
最近開始使用 Python3.X 了,最大的感受就是之前很多覺得很難用的標准庫經過這么多年的修改,已經變得好用了起來。例如我之前可能會為了達到多線程的效果去開很多進程或者使用 gevent 協程來幫助解決此類問題,但是現在
發現線程也很好用。使用方法也很簡單 👍
Reference:
https://www.dongwm.com/post/use-concurrent-futures/ 使用concurrent.futures的一些經驗
https://docs.python.org/zh-cn/3/library/concurrent.futures.html 啟動並行任務
https://slxiao.github.io/2019/06/10/mysql/ Python多線程程序中的MYSQL連接管理研究