1. 什么是執行緒
執行緒就是行程里面的執行單位,每一個行程肯定都自帶一個執行緒,真正被cpu執行的是執行緒,執行緒是代碼的執行程序,該程序中需要的資源都找對應的行程要
行程是資源的單位,執行緒是執行單位!

補充:同一個行程里面的多個執行緒資源是共享的!
2. 為啥要有執行緒
一個行程里面可以開設多個執行緒,而開設執行緒是不需要申請記憶體空間的(行程需要),因此,開設執行緒的消耗遠遠小于行程!
3. 如何使用執行緒
3.1 創建執行緒的第一種方式(掌握)
from threading import Thread # 匯入執行緒模塊
from multiprocessing import Process
import time
# 定義子執行緒函式
def func(name):
print('%s 來啦'%name)
time.sleep(2)
print('%s走了'%name)
# 創建執行緒不需要寫在main下,但是我們還是習慣性的把它寫在main下
if __name__ == '__main__':
# 創建執行緒物件
t = Thread(target=func,args=('zhang',))
# p = Process(target=func,args=('zhang',))
# p.start()
t.start() # 啟動執行緒
# 執行緒和行程的開啟相比,執行緒更加的塊,執行緒不需要開辟記憶體空間
print('主')
3.2 創建執行緒的第二種方式
from threading import Thread
# 1.創建一個類,該類繼承執行緒類
class MyThread(Thread):
# 第二種方法需要給子執行緒添加引數的方法是類中有一個雙init方法
# 先繼承父類的雙init方法,然后在書寫需要自己加入的引數,這樣就可以在run函式里呼叫引數了
def __init__(self,name):
super().__init__()
self.name = name
# 2.該類中子執行緒函式名必須是run
def run(self):
print('我是執行緒%s'%self.name)
if __name__ == '__main__':
# 3.創建子執行緒物件
t = MyThread('zhang')
# 4.開啟子執行緒
t.start()
print('主')
3.3 執行緒中的join方法
join方法的含義是:主執行緒等待子執行緒運行完畢再執行
from threading import Thread # 匯入執行緒模塊
from multiprocessing import Process
import time
def func(name):
print('%s 來啦'%name)
time.sleep(1)
print('%s走了'%name)
if __name__ == '__main__':
t = Thread(target=func,args=('zhang',))
t.start()
t.join() # join方法的含義是:主執行緒等待子執行緒運行完畢再執行
print('主')
3.4 執行緒物件和其他方法
from threading import Thread,active_count,current_thread
import time,os
def func():
# os.getpid()獲取當前執行緒的行程id,主執行緒和子執行緒id相同
# print('hello',os.getpid())
# current_thread().name獲取當前執行緒的執行緒名
print('hello', current_thread().name)
time.sleep(2)
if __name__ == '__main__':
t = Thread(target=func)
t.start()
# print('主',current_thread().name)
# active_count() 獲取當前活躍的執行緒數量
print('主',active_count())
3.5 守護執行緒
from threading import Thread
import time
def func(name):
print('%s來啦'%name)
time.sleep(2)
print('%s走了'%name)
if __name__ == '__main__':
t = Thread(target=func,args=('zhang',))
t.daemon = True # 守護主執行緒,主執行緒結束之后該子執行緒也會結束
t.start()
print('主')
"""
主執行緒一般不會自動結束,它會等待所有非守護執行緒結束之后再結束
因為主執行緒結束意外著主行程也結束了,這樣其他執行緒就無法呼叫主行程的資源了
"""
3.6 執行緒的互斥鎖
from threading import Thread,Lock
import time
momey = 100
# 加一把鎖
metua = Lock()
def func():
global momey
# 在獲取資料的前面加一把鎖
metua.acquire()
num = momey
time.sleep(0.01)
momey = num -1
# 釋放該鎖
metua.release()
if __name__ == '__main__':
t_list = []
for i in range(100):
t = Thread(target=func)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(momey)
3.7 GIL鎖與互斥鎖
GIL需要知道的重點:
1.GIL不是python的特點而是CPython解釋器的特點
2.GIL是保證解釋器級別的資料的安全
3.GIL會導致同一個行程下的多個執行緒的無法同時執行即無法利用多核優勢(******)
4.針對不同的資料還是需要加不同的鎖處理
5.解釋型語言的通病:同一個行程下多個執行緒無法利用多核優勢
GIL鎖與互斥鎖的區別
from threading import Thread,Lock
import time
momey = 100
# 加一把鎖
metua = Lock()
def func():
global momey
# 在獲取資料的前面加一把鎖
metua.acquire()
num = momey
time.sleep(0.01) # 注意:這里sleep是在模擬網路延遲,如果假設沒有網路延遲,沒有互斥鎖LOCK,得到money的結果也應為0,因為一個行程里有一個cpython解釋器,cpython解釋器有一個GIL鎖的概念,同一個行程下的多個執行緒無法同時執行,它們必須去搶同一個GIL鎖,沒有網路延遲(沒有IO),GIL鎖就會直到資料操作完在釋放,保證了多個執行緒執行的資料安全;但是實際上是有網路延遲的,就比如sleep,這時GIL鎖就會直接釋放掉,導致得到的money=99
momey = num -1
# 釋放該鎖
metua.release()
if __name__ == '__main__':
t_list = []
for i in range(100):
t = Thread(target=func)
t.start()
t_list.append(t)
for t in t_list:
t.join()
print(momey)
3.8多執行緒與多行程的比較
當需要處理的作業大都是是計算密集型時優先考慮多行程(較好的利用多核優勢)
當需要處理的作業大都是IO密集型時優先考慮多執行緒(節省空間消耗)
計算密集型就是計算處理偏多
IO密集型就是用戶輸入輸出偏多
# 當處理的作業是計算密集型的時候,好像現在多執行緒比多行程有優勢???待驗證!
from multiprocessing import Process
from threading import Thread
import os,time
def work():
res = 0
for i in range(1000000):
res *= i
if __name__ == '__main__':
list = [] # 將下面for回圈每次開設的執行緒/行程物件存盤起來
print(os.cpu_count()) # 獲取當前計算機的CPU個數
start_time = time.time() # 獲取開始時間
for i in range(16): # for回圈開設行程/執行緒
# p = Process(target=work)
p = Thread(target=work)
p.start()
list.append(p) # 將每次開設的行程物件添加到串列里
for p in list: # 變數行程物件
p.join() # 守護主行程/執行緒
print(time.time()-start_time)
# 計算密集型 多行程用時1.8412911891937256秒 多執行緒用時0.318603515625秒
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/502820.html
標籤:其他
上一篇:[PostgreSql]生產級別資料庫安裝要考慮哪些問題?
下一篇:類和物件
