一篇文章学习Python中的多线程
基本知識:
- 一、什么是多線程Threading
- 二、添加線程Thread
- 三、join功能
- 四、存儲進程結果Queue
- 五、GIL不一定有效率
- 六、線程鎖 Lock
一、什么是多線程Threading
二、添加線程Thread
import threading # 導入模塊print(threading.active_count()) # 獲取激活的線程數 print(threading.enumerate()) # 查看所有線程信息 print(threading.current_thread()) # 查看正在運行的線程def thread_job():print("This is thread of %s",threading.current_thread())if __name__ == '__main__':print("This is thread of %s",threading.current_thread())thread = threading.Thread(target=thread_job,) # 定義線程:注意target只寫函數名,不能帶括號thread.start() # 讓線程開始工作結果:
1 [<_MainThread(MainThread, started 4661450176)>] <_MainThread(MainThread, started 4661450176)> This is thread of %s <_MainThread(MainThread, started 4661450176)> This is thread of %s <Thread(Thread-1, started 123145514528768)>三、join功能
1. 不使用join()的結果:主線程Main thread 和子線程 Thread1 同時進行
import threading import time def thread_job():print("Thread1 start")time.sleep(1) # 模擬任務消耗時間print("Thread1 finish")if __name__ == '__main__':thread = threading.Thread(target=thread_job,name="Thread1") thread.start() print("All finish")預期結果:
Thread1 start Thread1 finish All finish實際結果:
Thread1 start All finish Thread1 finish2. 使用join()的結果:在子線程 Thread1 完成之后再執行主線程Main thread
if __name__ == '__main__':thread = threading.Thread(target=thread_job,name="Thread1") thread.start() thread.join()print("All finish")結果:
Thread1 start Thread1 finish All finish3. 如果是多個線程,為了規避不必要的麻煩,推薦1221的V型排布
thread_1.start() thread_2.start() thread_2.join() thread_1.join() print("All finish")四、存儲進程結果Queue
import threading import time from queue import Queue def thread_job(l,q): # l:列表,q:隊列,功能:計算列表每個元素的平凡,結果保存在隊列中for i in range(len(l)):l[i] = l[i]**2q.put(l) # 多線程調用的函數不能用return返回值,故用隊列保存結果if __name__ == '__main__':q = Queue()data = [[1,2,3],[4,5,6],[7,8,9]]threads = []for i in range(3): # 定義三個線程t = threading.Thread(target=thread_job,args=(data[i],q))t.start() # 開啟線程threads.append(t) # 把每個線程加入到線程列表中for thread in threads: # 將3個線程join到主線程中thread.join()for _ in range(3):print(q.get()) # 使用q.get()按順序從q中取出結果結果:
[1, 4, 9] [16, 25, 36] [49, 64, 81]五、GIL不一定有效率
python 多線程有時候并不理想,主要原因是python的設計上有一個必要環節Global Interpreter Lock(GIL)全局鎖。使得python還是一次性只能處理一個東西。
盡管Python完全支持多線程編程, 但是解釋器的C語言實現部分在完全并行執行時并不是線程安全的。
實際上,解釋器被一個全局解釋器鎖保護著,它確保任何時候都只有一個Python線程執行。
GIL最大的問題就是Python的多線程程序并不能利用多核CPU的優勢 (比如一個使用了多個線程的計算密集型程序只會在一個單CPU上面運行)。
在討論普通的GIL之前,有一點要強調的是GIL只會影響到那些嚴重依賴CPU的程序(比如計算型的)。
如果你的程序大部分只會涉及到I/O,比如網絡交互,那么使用多線程就很合適,因為它們大部分時間都在等待。
實際上,你完全可以放心的創建幾千個Python線程, 現代操作系統運行這么多線程沒有任何壓力,沒啥可擔心的。
當任務執行10次時普通順序調用和多線程消耗時間:
normal_time: 0.014399051666259766 multiThreading_time: 0.00513005256652832按理來說,多線程應該比普通方法速度快10倍,因為建立了10個線程,但是結果并沒有。這就是其中的GIL在作怪。
六、線程鎖 Lock
1. 不使用 Lock 的情況
在一個線程執行運算修改共享內存的過程中,其他線程也可以訪問該共享內存,會造成共享內存數據的混亂。
結果:
Job1: 1 Job1: 2 Job1: 13 Job2: 12 Job1: 14 Job2: 24 Job1: 25 Job2: 35 Job1: 36 Job2: 46 Job1: 47 Job2: 57 Job1: 58 Job2: 68 Job1: 69 Job2: 79 Job1: 80 Job2: 90 Job2: 100 Job2: 1102. 使用 Lock 的情況
Lock 在不同線程使用同一共享內存時,能夠確保線程之間互不影響
方法:在每個線程執行運算修改共享內存之前,執行 lock.acquare() 將共享內存上鎖,確保當前線程訪問時,共享內存不會被其他線程訪問,執行運算完畢后,使用 lock.release() 將鎖打開,保證其他線程可以使用該共享內存。
結果:
Job1: 1 Job1: 2 Job1: 3 Job1: 4 Job1: 5 Job1: 6 Job1: 7 Job1: 8 Job1: 9 Job1: 10 Job2: 20 Job2: 30 Job2: 40 Job2: 50 Job2: 60 Job2: 70 Job2: 80 Job2: 90 Job2: 100 Job2: 110學習視頻連接:?莫煩PYTHON
總結
以上是生活随笔為你收集整理的一篇文章学习Python中的多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 翻译:Docker方式安装redmine
- 下一篇: leetcode--数组(Medium1