Python编程基础:第五十八节 线程Threading
第五十八節 線程Threading
- 前言
- 實踐
前言
線程就是一個獨立的代碼執行流程,在一個線程內部Python會按照先后順序執行指定的代碼流。這里我們思考一下,如果我們創建多個線程,并為每個線程指定不同的任務不就可以實現并發操作了嗎?這種設計方式就像工廠上班一樣,我可以雇傭很多人然后把他們分為多個小組例如A、B、C,其中A組去制作產品的控制板、B組去制作產品的殼子、C組負責裝配。我們同時調度三組員工并行執行任務一定比調度一組員工先做控制板,再做殼子,最后裝配來得快。與實際情況不同的是,Python的GIL(global interpreter lock)設計使得在任何時候都只允許一個線程掌握Python解釋器的控制權,那么我們只有通過協調每個線程輪流運行以實現并發性。
CPU bound:程序/任務大部分時間都在等待內部事件(計算密集型),使用多進程(multiprocessing)
IO bound:程序/任務大部分時間都在等待外部事件(用戶輸入,網頁抓取),使用多線程(multithreading)
一般而言,多線程比多進程更加有效
實踐
為了使用多線程,我們首先需要導入模塊
import threading現在我們思考這樣一個場景,你在早上上學前需要做三件事情,分別是吃早餐、喝咖啡、以及補作業,其中吃早餐需要3秒,喝咖啡需要4秒,補作業需要5秒,我們寫函數來實現這三個任務:
import timedef eat_breakfast():time.sleep(3)print("eat breakfast finish")def drink_coffee():time.sleep(4)print("drink coffee finish")def study():time.sleep(5)print("study finish")如果不使用多線程,那我們只能按照順序執行三個任務
start = time.time() eat_breakfast() drink_coffee() study() end = time.time() print('總用時: {} 秒'.format(end-start)) >>> eat breakfast finish >>> drink coffee finish >>> study finish >>> 總用時: 12.00914740562439 秒不難發現程序按照順序執行三個函數,總用時時間大約等于各個函數執行時間之和,那我們如何使用多線程來并行化這三項任務呢,代碼如下
start = time.time() x = threading.Thread(target=eat_breakfast, args=()) x.start() y = threading.Thread(target=drink_coffee, args=()) y.start() z = threading.Thread(target=study, args=()) z.start() end = time.time() print('總用時: {} 秒'.format(end-start)) >>> 總用時: 0.0018167495727539062 秒 >>> eat breakfast finish >>> drink coffee finish >>> study finish這里會發現一個奇怪的現象,先打印出代碼的執行時間,然后打印各個函數的輸出,貌似是首先執行了print()語句,然后執行三個函數。這是因為time模塊是給主線程計時,而主線程的任務就是創建三個子線程,所以它能夠在極短的時間內運行完,而各個子線程各自運行完分別打印其輸出即可。這里可以想象成一棵樹,樹干是主線程,樹枝是三個子線程,他們之間的生長速度是相互獨立的,樹干(主線程)停止生長不影響樹枝(子線程)的生長速度。那是否有一種方式使主線程等待所有子線程執行完呢,其實是可以的:
start = time.time() x = threading.Thread(target=eat_breakfast, args=()) x.start() y = threading.Thread(target=drink_coffee, args=()) y.start() z = threading.Thread(target=study, args=()) z.start() x.join() y.join() z.join() end = time.time() print('總用時: {} 秒'.format(end-start)) >>> eat breakfast finish >>> drink coffee finish >>> study finish >>> 總用時: 5.015807390213013 秒通過join()函數便可使主線程等待子線程結束后再結束,此時主線程的執行時間幾乎與最慢子線程執行時間相同。當然我們還可以查看當前的線程數目以及各個線程的名稱:
x = threading.Thread(target=eat_breakfast, args=()) x.start() y = threading.Thread(target=drink_coffee, args=()) y.start() z = threading.Thread(target=study, args=()) z.start() print(threading.active_count()) # 打印線程個數 print(threading.enumerate()) # 打印線程名稱 print(time.perf_counter()) # 打印主線程執行時間 >>> 4 >>> [<_MainThread(MainThread, started 18896)>, <Thread(Thread-1, started 15528)>, <Thread(Thread-2, started 5748)>, <Thread(Thread-3, started 7500)>] >>> 0.7035468 >>> eat breakfast finish >>> drink coffee finish >>> study finish本節完整代碼如下:
import threading import timedef eat_breakfast():time.sleep(3)print("eat breakfast finish")def drink_coffee():time.sleep(4)print("drink coffee finish")def study():time.sleep(5)print("study finish")x = threading.Thread(target=eat_breakfast, args=()) x.start() y = threading.Thread(target=drink_coffee, args=()) y.start() z = threading.Thread(target=study, args=()) z.start()print(threading.active_count()) # 打印線程個數 print(threading.enumerate()) # 打印線程名稱x.join() y.join() z.join()print(time.perf_counter()) # 打印主線程執行時間 >>> 4 >>> [<_MainThread(MainThread, started 7264)>, <Thread(Thread-1, started 8356)>, <Thread(Thread-2, started 18672)>, <Thread(Thread-3, started 14300)>] >>> eat breakfast finish >>> drink coffee finish >>> study finish >>> 5.4419603總結
以上是生活随笔為你收集整理的Python编程基础:第五十八节 线程Threading的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Python编程基础:第五十七节 red
- 下一篇: Python编程基础:第五十九节 守护线