<Notes>Python_Multithreading
- 前言
- 學習筆記
- theading核心函式大致用法
- 添加執行緒:
- join函式:
- Queue功能:
- GIL機制:
- lock用法:
- 其他總結
- 其他思考
- 學習總結
- 參考資料
- 視頻資料
- 檔案資料
- 官方資料
前言
基本學習目標
- 學習Python多執行緒基本語法
- 鞏固Python編程知識
- 實際設計:設計多執行緒收發通信程式
學習筆記
theading核心函式大致用法
添加執行緒:
代碼如下:
import threading
def thread_job():
#查看運行當前程式的執行緒
print('This is a thread of %s \n'%threading.current_thread()) # \反斜杠,/正斜杠
#查看當前執行緒數
print(threading.active_count())
def main():
t1 = threading.Thread(target = thread_job)
t1.start()
#print(threading.active_count()) 這行代碼不能放在這,因為主執行緒和t1執行緒不知道誰執行的快,所以有時輸出1有時輸出2
if __name__ == '__main__':
main()
#output:
This is a thread of <Thread(Thread-1, started 1028)>
2
join函式:
- 功能描述:控制執行緒執行
- 用法描述:在執行緒1中對執行緒2使用join方法,執行緒1會停在此處等待執行緒2執行完成之后再執行;
- 代碼分析:
import threading
import time
def t1_job():
print('t1 start\n')
for i in range(10):
time.sleep(0.1)
print('t1 finish\n')
def t2_job():
print('t2 start\n')
print('t2 finish\n')
def main():
t1 = threading.Thread(target=t1_job,name='t1') #這里如果函式加(),程式會先執行一遍函式
t2 = threading.Thread(target=t2_job,name='t2')
t1.start()
t2.start()
t1.join()
t2.join()
print('all done\n')
if __name__ == '__main__':
main()
Queue功能:
- 功能描述:多執行緒的運算結果是不能有回傳值的,queue模塊實作了各種【多生產者-多消費者】佇列,可用于在執行的多個執行緒之間安全的交換資訊,
- 用法描述:把各個執行緒的運算結果,放進一個常用的佇列之中,再到主執行緒拿出來,
- 代碼分析:
import threading
import time
from queue import Queue
def job(l,q):
for i in range(len(l)):
l[i] = l[i]**2
q.put(l) #put是Queue的方法
def multthreading():
q = Queue()
threads = []
data = [[1,2,3],[3,4,5],[2,2,2],[5,5,5]]
for i in range(4):
t = threading.Thread(target=job,args=[data[i],q])
t.start()
threads.append(t)
for thread in threads:
thread.join()
result = []
for _ in range(4):
result.append(q.get())
print(result)
if __name__ == '__main__':
multthreading()
GIL機制:
-
問題描述:GIL(Global Interpreter Lock),一個時間只有一個執行緒在運算;
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once.
This lock is necessary mainly because CPython’s memory management is not thread-safe.
(However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

- 代碼分析:
import threading
from queue import Queue
import copy
import time
def job(l, q):
res = sum(l)
q.put(res)
def multithreading(l):
q = Queue()
threads = []
for i in range(4):
t = threading.Thread(target=job, args=(copy.copy(l), q), name='T%i' % i)
t.start()
threads.append(t)
[t.join() for t in threads]
total = 0
for _ in range(4):
total += q.get()
print(total)
def normal(l):
total = sum(l)
print(total)
if __name__ == '__main__':
l = list(range(1000000))
s_t = time.time()
normal(l*4)
print('normal: ',time.time()-s_t)
s_t = time.time()
multithreading(l)
print('multithreading: ', time.time()-s_t)
-
結果分析:
輸出:normal: 0.15059614181518555 multithreading: 0.14162278175354004
分析:為什么節約了一點點時間?扣除了讀寫(IO)的時間,
單執行緒下有 IO操作會進行 IO等待,造成不必要的時間浪費,而開啟多執行緒能在 執行緒 A等待時,自動切換到執行緒 B,可以不浪費 CPU的資源,從而能提升程式執行效率 ;
python中想要某個執行緒要執行必須先拿到 GIL這把鎖,且 python只有一個 GIL,拿到這個 GIL才能進入 CPU執行, 在遇到 I/O 操作時會釋放這把鎖,如果是純計算的程式,沒有 I/O 操作,解釋器會每隔 100次操作就釋放這把鎖,讓別的執行緒有機會 執行(這個次數可以通sys.setcheckinterval來調整),所以雖然 CPython 的執行緒庫直接封裝作業系統的原生執行緒,但 CPython 行程做為一個整體,同一時間只會有一個獲得了 GIL 的執行緒在跑,其它的執行緒都處于等待狀態等著 GIL 的釋放,
總結:進行 IO密集型的時候可以進行分時切換 所有這個時候多執行緒快過單執行緒
lock用法:
-
功能描述:對于多個執行緒同時需要訪問一個變數,lock可以控制鎖住變數,(執行緒)不互相干擾
-
用法描述:mutex = threading.Lock() # 創建鎖mutex.acquire([timeout]) # 鎖定mutex.release() # 釋放
-
代碼分析:
import threading import time def job1(): time.sleep(5) global A,lock lock.acquire() for i in range(10): A += 1 print('job',A) lock.release() def job2(): global A,lock lock.acquire() for i in range(10): A += 10 print('job2',A) lock.release() if __name__ == '__main__': lock = threading.Lock() A = 0 t1 = threading.Thread(target=job1()) t2 = threading.Thread(target=job2()) t1.start() t2.start() t1.join() t2.join()
結果分析:
- 問題1:這里有一個問題,lock的作用還是說鎖住了這個變數的相關代碼還是鎖住了整個呼叫代碼的執行緒?
- 分析:lock鎖住了整個呼叫變數的執行緒,執行緒start順序決定lock順序
- 問題2:有了GIL還需要lock嗎?
- 分析:lock讓執行緒程式更加順序的執行;
- 問題3:lock的本質是join嗎
- 分析:不是;
其他總結
并發編程分為多執行緒和多行程;Python的多執行緒是并發而不是并行(多行程是并行);并發和并行從宏觀上來講都是同時處理多路請求的概念; 但并發和并行又有區別,并行是指兩個或者多個事件在同一時刻發生;而并發是指兩個或多個事件在同一時間間隔內發生,
其他思考
問題1:理論上來說,python程式可以虛擬出任意數量的執行緒,但如何選擇最優執行緒數呢?
分析:這里多執行緒并不能加速運算程序,所以無最優執行緒數一說;這個問題可以留給多行程;
問題2:當代碼中有多個執行緒時,需要多個join,這時候依次加入各個函式的join,這時候主執行緒是否會卡在第一個join?
分析:是這樣的,但是這并不妨礙多個執行緒運行,
問題3:執行緒阻塞的概念
分析:阻塞執行緒的情況下,程式會先等待執行緒任務執行完,再往下執行其他代碼;其實就是join;
學習總結
-
函式,物件,模塊的區別要記住;python中Object物件是大寫的;函式或者都是小寫的;
-
Pycharm中快捷鍵“ctrl+/”注釋選中的代碼;
-
行程是從 main() 方法開始的;
-
在創建執行緒中,輸入函式名和輸入函式名()的區別,輸入函式名()就相當于執行完次函式再運行;
參考資料
視頻資料
1、【莫煩Python】Threading 學會多執行緒 Python :https://www.bilibili.com/video/BV1jW411Y7Wj.
- 莫煩Python github: https://github.com/MorvanZhou/tutorials.
- 多執行緒部分github: https://github.com/MorvanZhou/tutorials/tree/master/threadingTUT.
檔案資料
https://www.runoob.com/python3/python3-multithreading.html.
官方資料
https://docs.python.org/3/library/threading.html.
筆記github鏈接: https://github.com/GRF-Sunomikp31/WorkSpace/blob/main/Python/Python%20Multithreading.md.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/266678.html
標籤:python
上一篇:Python批量獲取基金資料
