Python學習 :多線程 --- 鎖

来源:https://www.cnblogs.com/ArticleYeung/archive/2019/04/08/10668568.html
-Advertisement-
Play Games

多線程 什麼是鎖? - 鎖通常被用來實現對共用資源的同步訪問。 - 為每一個共用資源創建一個Lock對象,當你需要訪問該資源時,調用acquire方法來獲取鎖對象(如果其它線程已經獲得了該鎖,則當前線程需等待其被釋放),待資源訪問完後,再調用release方法釋放鎖: GIL(Global Inte ...


 多線程

  什麼是鎖?

  - 鎖通常被用來實現對共用資源的同步訪問

  - 為每一個共用資源創建一個Lock對象,當你需要訪問該資源時,調用acquire方法來獲取鎖對象(如果其它線程已經獲得了該鎖,則當前線程需等待其被釋放),待資源訪問完後,再調用release方法釋放鎖:

  GIL(Global Interpreter Lock) 全局的解釋器鎖

  增加鎖的目的:

  1.雖然效率十分低,但保證了數據的安全性

  2.不同的鎖對應保護不同的數據

  3.誰拿到GIL鎖就讓誰得到Cpython解釋器的執行許可權

  4.GIL鎖保護的是Cpython解釋器數據的安全,而不會保護你自己程式的數據的安全

  5.GIL鎖當遇到阻塞的時候,就被迫把鎖給釋放了,那麼其他的就開始搶鎖了,搶到後把值進行修改,但是第一個拿到鎖的還依舊保持著原本的數據,當再次拿到鎖的時候,數據已經修改了,而第一位拿的還是原來的數值,這樣就造成了混亂,也就保證不了數據的安全了。

  同步鎖Lock

  - GIL與Lock是兩把鎖,保護的數據不一樣,前者是解釋器級別的(保護的是解釋器級別的數據,比如垃圾回收的數據),後者是保護用戶自己開發的應用程式的數據

  實例:沒有加上鎖的情況

  - 在執行這個操作的多條bytecodes期間的時候可能中途就換別的線程了,這樣就出現了data races的情況

mport time
import threading
def addNum():
    global num # 在每個線程中都獲取這個全局變數
    temp = num
    print('--get num:',num )
    time.sleep(0.01)
    num = temp - 1 # 對此公共變數進行-1操作

start = time.time()
num = 100  # 設定一個共用變數
thread_list = []
for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: # 等待所有線程執行完畢
    t.join()

print('final num:', num )
end = time.time()
print(end - start)
# 此時並不能獲取到正確的答案0
# 100個線程每一個一定都沒有執行完就進行了切換,我們說過sleep就等效於IO阻塞,1s之內不會再切換回來,所以最後的結果一定是99.
# 多個線程都在同時操作同一個共用資源,所以造成了資源破壞

  實例:加上鎖的情況

  - 同步鎖保證了在同一時刻只有一個線程被執行

import time
import threading

def addNum():
    global num # 在每個線程中都獲取這個全局變數
    lock.acquire() # 獲取鎖
    temp = num
    print('--get num:',num )
    num = temp - 1 # 對此公共變數進行-1操作
    lock.release() # 只有在執行完上述內容才會釋放鎖

start = time.time()
num = 100  # 設定一個共用變數
thread_list = []
lock = threading.Lock()

for i in range(100):
    t = threading.Thread(target=addNum)
    t.start()
    thread_list.append(t)

for t in thread_list: #等待所有線程執行完畢
    t.join()

print('final num:', num )
end = time.time()
print(end - start)

  死鎖Lock

  線程間共用多個資源的時候,如果兩個線程分別占有一部分資源並且同時等待對方的資源,就會造成死鎖的情況

  因為系統判斷這部分資源都正在使用,所有這兩個線程在無外力作用下將一直等待下去

import threading,time

class myThread(threading.Thread):
    def LoA(self):
        lockA.acquire()
        print(self.name,"got lockA",time.ctime())
        time.sleep(3)
        lockB.acquire()
        print(self.name,"got lockB",time.ctime())
        lockB.release()
        lockA.release()
    def LoB(self):
        lockB.acquire()
        print(self.name,"got lockB",time.ctime())
        time.sleep(2)
        lockA.acquire()
        print(self.name,"got lockA",time.ctime())
        lockA.release()
        lockB.release()
    def run(self):
        self.LoA()
        self.LoB()

if __name__=="__main__":
    lockA=threading.Lock()
    lockB=threading.Lock()
    # 解決方案:添加 lock = threading.RLock()
    # lock = threading.RLock()
    threads=[]
    for i in range(3):
        threads.append(myThread())
    for t in threads:
        t.start()
    for t in threads:
        t.join()
    # 此時線程會卡死,一直等待下去,此時添加遞歸鎖即可解決死鎖的問題

  遞歸鎖 RLock

  在添加遞歸鎖後,RLock內部維護著一個Lock和一個counter變數,counter記錄了acquire的次數,從而使得資源可以被多次acquire。直到一個線程所有的acquire都被release,其他的線程才能獲得資源

  信號量

  信號量用來控制線程併發數的,它也是一把鎖,BoundedSemaphore或Semaphore管理一個內置的計數 器,每當調用acquire()時-1,調用release()時+1

  計數器不能小於0,當計數器為 0時,acquire()將阻塞線程至同步鎖定狀態,直到其他線程調用release()。(類似於停車位的概念)

  BoundedSemaphore與Semaphore的唯一區別在於前者將在調用release()時檢查計數 器的值是否超過了計數器的初始值,如果超過了將拋出一個異常

import threading,time
class myThread(threading.Thread):
    def run(self):
        if semaphore.acquire(): # 如果上信號量鎖就往下進行
            print(self.name)
            time.sleep(5)
            semaphore.release()
if __name__=="__main__":
    semaphore = threading.Semaphore(5) # 一次允許5個線程進行
    # semaphore = threading.BoundedSemaphore(5) # 與上述效果一致
    thrs = []
    for i in range(20): # 開啟20個線程
        thrs.append(myThread())
    for t in thrs:
        t.start()

  條件變數同步

  有一類線程需要滿足條件之後才能夠繼續執行,Python提供了threading.Condition對象用於條件變數線程的支持,它除了能提供RLock()或Lock()的方法外,還提供了wait()、notify()、notifyAll()方法

  lock_con = threading.Condition([Lock / Rlock]): 預設創建一個RLock鎖,用於線程間的通信

  wait():條件不滿足時調用,線程會釋放鎖併進入等待阻塞

  notify():條件創造後調用,通知等待池激活一個線程

  notifyAll():條件創造後調用,通知等待池激活所有線程

import threading,time
from random import randint

class Producer(threading.Thread):
    def run(self):
        global L
        while True:
            val=randint(0,100)
            print('生產者',self.name,":Append"+str(val),L)
            if lock_con.acquire():
                L.append(val)
                lock_con.notify() # 激活一個線程
                lock_con.release()
            time.sleep(3)
class Consumer(threading.Thread):
    def run(self):
        global L
        while True:
                lock_con.acquire() # wait()過後,從此處開始進行
                if len(L)==0:
                    lock_con.wait() # 進入等待阻塞
                    print('繼續進行') # 我們可以看到並沒有列印這個話,這說明線程不是從wait()下麵繼續進行
                print('消費者',self.name,":Delete"+str(L[0]),L)
                del L[0]
                lock_con.release()
                time.sleep(0.25)

if __name__=="__main__":

    L = []
    lock_con = threading.Condition()
    threads = []
    for i in range(5):
        threads.append(Producer())
    threads.append(Consumer())
    for t in threads:
        t.start()
    for t in threads:
        t.join()

  同步條件(Event)

  條件同步和條件變數同步差不多意思,只是少了鎖功能,同步條件不是鎖

  因為條件同步設計於不訪問共用資源的條件環境。event = threading.Event():條件環境對象,初始值為False;

  event.isSet():返回event的狀態值

  event.wait():如果 event.isSet()==False將阻塞線程

  event.set(): 設置event的狀態值為True,所有阻塞池的線程激活進入就緒狀態, 等待操作系統調度

  event.clear():恢復event的狀態值為False

import threading,time
import random
def light():
   if not event.isSet():
        event.set() #wait就不阻塞 #綠燈狀態
    count = 0
while True: if count < 10: print('\033[42;1m--green light on---\033[0m') elif count <13: print('\033[43;1m--yellow light on---\033[0m') elif count <20: if event.isSet(): event.clear() print('\033[41;1m--red light on---\033[0m') else: count = 0 event.set() # 打開綠燈 time.sleep(1) count += 1 def car(n): while 1: time.sleep(random.randrange(10)) if event.isSet(): # 綠燈 print("car [%s] is running.." % n) else: print("car [%s] is waiting for the red light.." %n) if __name__ == '__main__': event = threading.Event() Light = threading.Thread(target=light) Light.start() for i in range(3): t = threading.Thread(target=car,args=(i,)) t.start()

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 死磕 java集合之ConcurrentHashMap源碼分析(一) 它的存儲結構是什麼樣的? 它使用了哪些鎖? 它是怎麼擴容的? 它是否是強一致性的? 它不能解決哪些問題? 它的源碼中使用了哪些不常見的技術? ...
  • '''迭代器:兩個基本方法:iter()和next()迭代器是一個可以記住遍歷的位置的對象。 迭代器對象從集合等第一個元素開始訪問,直到所有的元素被訪問結束,迭代器只能往前不會後退。 迭代器有兩個基本的方法:iter()和next() 字元串,列表或元組對象都可以用於創建迭代器。 迭代器的一大優點是 ...
  • 1. 調用鏈Cat 1.1. 調用鏈演進 1.2. 開源產品比較 1.3. 監控場景 1.4. cat的增值作用 1.5. cat典型報表 1.5.1. 應用報錯大盤 1.5.2. 業務大盤 1.5.3. logView 1.5.4. 可視化的logView 1.5.5. 應用報表(APM) 1.5 ...
  • 一、請求鉤子 在客戶端和伺服器交互的過程中,有些準備工作或稍微工作是需要處理的,比如:在請求開始時,建立資料庫連接;在請求開始時,根據需求進行許可權校驗;在請求結束時,指定數據的交互格式等。為了讓每個視圖函數避免編寫重覆功能的代碼,flask提供了通用設施的功能,即請求鉤子。 例子: 運行結果: 第1 ...
  • 概念 公平鎖/非公平鎖 公平鎖是指多個線程按照申請鎖的順序來獲取鎖。 非公平鎖是指多個線程獲取鎖的順序並不是按照申請鎖的順序,有可能後申請的線程比先申請的線程優先獲取鎖。有可能,會造成優先順序反轉或者饑餓現象。 對於 Java ReentrantLock而言,通過構造函數指定該鎖是否是公平鎖,預設是非 ...
  • "概要" "schema" "changeset" "struct" "map" "總結" 概要 Ecto 中, 對資料庫的操作中經常用到 4 個類型: schema changeset struct map 在 Ecto 的 API 中, 基本都是都是對這 4 個類型的操作, 這 4 個類型的關係 ...
  • (1) np.mashgrid()函數: 生成網路點坐標矩陣,可以是二維網路矩陣,也可以是三維網路矩陣。其中,每個交叉點就是網路點,描述這些網路點的矩陣就是坐標矩陣(橫坐標矩陣X中的每個元素與縱坐標矩陣Y中對應位置元素,共同構成一個點的完整坐標)。 背景示例:網路點與坐標矩陣的解釋如下: impor ...
  • 之前在CSDN上也寫了幾篇技術的博客,大部分在Onenote裡面記錄學習過程,在寫筆記中也大量參閱CNblog和CSDN, 突然發現CNblog的界面清晰簡潔,因此發佈在網上也可以幫助到更多的人解決學習過程中類似的問題。 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...