1. 程式人生 > >Python中的執行緒鎖

Python中的執行緒鎖

Python中的執行緒鎖

前言

  本章節繼續探討threading模組下關於鎖的應用,注意。這一期很重要,依然是圍繞著理論篇來講,這一章節主要圍繞理論篇中的執行緒切換做講解,因此一定要有一些執行緒切換方面的知識。

  官方中文文件

執行緒安全

 

  執行緒安全是多執行緒程式設計時的計算機程式程式碼中的一個概念。在擁有共享資料的多條執行緒並行執行的程式中,執行緒安全的程式碼會通過同步機制保證各個執行緒都可以正常且正確的執行,不會出現資料汙染等意外情況。

 

  在聊執行緒安全之前,我還是決定拿之前的那個例子來描述它。

 

  執行緒安全的問題還是由於執行緒切換導致的,比如這個房間(程序)一共有10顆糖(資源),一個小人(子執行緒)吃了3顆糖被CPU通知歇息一會,那麼他會認為還剩下7顆糖,另一個幹活的小人又吃了3顆糖後去休息了,那麼現在第一個休息的小人上線了,但是真正的糖果數量只剩下了4顆,他還傻乎乎的認為是7顆。

 

  這裡關於執行緒安全的程式碼測試我決定不用上面例舉的這個例子,而是用多執行緒做密集型計算做演示,數值越大效果越明顯:

import threading

num = 0
def add():
    global num
    for i in range(10000000):  # 一千萬次
        num += 1

def sub():
    global num
    for i in range(10000000):  # 一千萬次
        num -= 1

if __name__ == '__main__':


    t1 = threading.Thread(target=add,)
    t2 = threading.Thread(target=sub,)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print("最終結果:",num)

# ==== 執行結果 ==== 三次採集

"""
最終結果: -1472151
最終結果: -2814372
最終結果: -5149396
"""

 

  一個加一千萬次,一個減一千萬次,按理來說最後的結果應該是0,但是為什麼相差這麼大?更加恐怖的是每次的結果都完全不一樣呢?

  其實這就是由於執行緒切換導致出的執行緒安全問題,因為我們不能精確的知道CPU會在什麼時候進行執行緒的切換,那麼如何控制它呢?我們就需要用到鎖,其實通俗點來講你可以這麼認為他:

 

  執行緒安全的目標:

    執行緒之間共同操作一個資源 如何保持資源的同步性

    因為執行緒的切換是由CPU控制的,所以我們要控制它的切換

 

  執行緒安全的定義:

    執行緒安全:多執行緒操作時,內部會讓所有執行緒排隊處理

    執行緒不安全:我們需要一個機制來讓所有執行緒進行排隊處理

 

  那麼什麼時候我們要考慮執行緒安全的問題呢?

    多個執行緒對同一資料來源進行寫入操作時

 

  Ps:在CPython的八大基本資料型別中,listdict本身就是屬於執行緒安全的容器。

 

鎖的作用

  鎖就提供給我們能夠自行操控執行緒切換的一種手段,而並非系統自帶的切換機制進行切換。

 

Lock同步鎖

方法大全


 

Lock同步(互斥)鎖方法方法大全  
方法/屬性名稱 功能描述
acquire(blocking=True, timeout=-1) 上鎖,在上鎖狀態中的程式碼塊執行時不允許切換至其他執行緒執行。
release() 解鎖,解鎖後系統可以切換至其他執行緒執行。
locked() 如果獲得了鎖則返回真值。

 

 

使用方式


 

  被上鎖和解鎖期間的程式碼塊執行時,不會切換至其他執行緒,有一點要注意的是對於lock鎖而言,一次acquire()必須對應一次release(),不能出現重複的使用兩次acquire()的操作,這會造成死鎖!

  注意:同步鎖是一次只能放行一個執行緒。

 

import threading

num = 0


def add():
    lock.acquire()  # 上鎖
    global num
    for i in range(10000000):  # 一千萬次
        num += 1
    lock.release()  # 解鎖


def sub():
    lock.acquire()  # 上鎖
    global num
    for i in range(10000000):  # 一千萬次
        num -= 1
    lock.release()  # 解鎖


if __name__ == '__main__':
    lock = threading.Lock()  # 例項化同步鎖物件

    t1 = threading.Thread(target=add, )
    t2 = threading.Thread(target=sub, )
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print("最終結果:", num)

# ==== 執行結果 ==== 三次採集

"""
最終結果: 0
最終結果: 0
最終結果: 0
"""
import threading

num = 0
def add():
    lock.acquire()  # 上鎖
    lock.acquire()  # 死鎖
    global num
    for i in range(10000000):  # 一千萬次
        num += 1
    lock.release()
    lock.release()

def sub():
    lock.acquire()  # 上鎖
    lock.acquire()  # 死鎖
    global num
    for i in range(10000000):  # 一千萬次
        num -= 1
    lock.release()
    lock.release()  

if __name__ == '__main__':

    lock = threading.Lock() # 例項化同步鎖物件

    t1 = threading.Thread(target=add,)
    t2 = threading.Thread(target=sub,)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print("最終結果:",num)

# ==== 執行結果 ==== 三次採集

"""
卡住不動了
"""
Lock同步鎖的死鎖現象

 

上下文管理


  threading.Lock()物件中實現了__enter____exit__方法,因此我們可以使用with語句進行上下文管理式的加鎖。

import threading

num = 0
def add():
    with lock:  # 自動加鎖與解鎖
        global num
        for i in range(10000000):  # 一千萬次
            num += 1


def sub():
    with lock:  # 自動加鎖與解鎖
        global num
        for i in range(10000000):  # 一千萬次
            num -= 1


if __name__ == '__main__':

    lock = threading.Lock()

    t1 = threading.Thread(target=add,)
    t2 = threading.Thread(target=sub,)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print("最終結果:",num)

# ==== 執行結果 ==== 三次採集

"""
最終結果: 0
最終結果: 0
最終結果: 0
"""
with語句的使用

 

RLock遞迴鎖

方法大全


Rlock遞迴鎖方法方法大全  
方法/屬性名稱 功能描述
acquire(blocking=True, timeout=-1) 上鎖,在上鎖狀態中的程式碼塊執行時不允許切換至其他執行緒執行。如果已上鎖,遞迴層數增加一層。
release() 解鎖,解鎖後系統可以切換至其他執行緒執行。

 

使用方式


 

  RLock遞迴鎖的使用方式與同步鎖相同,唯一的不同點就是可以多次acquire(),這並不會產生死鎖,但是有幾次acquire()就應該有相應的幾次release(),否則依然會造成死鎖!

  注意:遞迴鎖與同步鎖相同,也是一次只能放行一個執行緒。並且也支援上下文管理。

 

import threading

num = 0
def add():
    lock.acquire()  # 上鎖 + 1
    lock.acquire()  # 上鎖 + 1
    global num
    for i in range(10000000):  # 一千萬次
        num += 1
    lock.release()  # 解鎖 - 1
    lock.release()  # 解鎖 - 1

def sub():
    lock.acquire()  # 上鎖
    lock.acquire()  # 死鎖
    global num
    for i in range(10000000):  # 一千萬次
        num -= 1
    lock.release()  # 解鎖 - 1
    lock.release()  # 解鎖 - 1

if __name__ == '__main__':

    lock = threading.RLock()  # 例項化遞迴鎖物件

    t1 = threading.Thread(target=add,)
    t2 = threading.Thread(target=sub,)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print("最終結果:",num)

# ==== 執行結果 ==== 三次採集

"""
最終結果: 0
最終結果: 0
最終結果: 0
"""

 

Condition條件鎖

方法大全


 

Condition條件鎖方法方法大全  
方法/屬性名稱 功能描述
acquire(blocking=True, timeout=-1) 上鎖,在上鎖狀態中的程式碼塊執行時不允許切換至其他執行緒執行。
release() 解鎖,解鎖後系統可以切換至其他執行緒執行。
wait(timeout=None) 等待喚醒,此時該執行緒是處於暫停執行狀態,要麼等待通知要麼等待到超時時間後繼續執行該執行緒。
wait_for(predicate, timeout=None) 等待喚醒,直到返回了一個Truepredicate是一個可呼叫的物件,其返回值應該是True或者False
notify(n=1) 通知喚醒,可以喚醒多個處於wait()的執行緒,預設為1個。
notify_all() 通知喚醒,喚醒所有處於wait()的執行緒。

 

使用方式


 

  Condition條件鎖是在遞迴鎖的基礎上增加了能夠暫停執行緒執行的功能。並且我們可以使用wait()notify()來控制每個執行緒執行的個數。

  注意:條件鎖可以自由設定一次放行幾個執行緒。

 

import threading

num = 0

def task():
    obj = threading.current_thread()
    print("當前是執行緒[{0}],已經開始運行了...".format(obj.getName()))
    cond.acquire()  # 上鎖
    global num
    print("當前是執行緒[{0}],處於等待狀態...".format(obj.getName()))
    cond.wait()  # 暫停,等待喚醒
    num += 1
    print("當前是執行緒[{0}],等待狀態結束,繼續執行...".format(obj.getName()))
    cond.release()  # 解鎖

if __name__ == '__main__':

    cond = threading.Condition()  # 例項條件鎖物件

    for i in range(10):
        t1 = threading.Thread(target=task,)  # 開啟10條執行緒
        t1.start()  # 等待CPU排程執行

    while num < 10:
        task_num = int(input("請輸入你要執行的執行緒數量:"))

        cond.acquire()
        cond.notify(task_num)  # 通知喚醒
        cond.release()

    print("最終結果:",num)

# ==== 執行結果 ====

"""
當前是執行緒[Thread-1],已經開始運行了...
當前是執行緒[Thread-1],處於等待狀態...
當前是執行緒[Thread-2],已經開始運行了...
當前是執行緒[Thread-2],處於等待狀態...
當前是執行緒[Thread-3],已經開始運行了...
當前是執行緒[Thread-3],處於等待狀態...
當前是執行緒[Thread-4],已經開始運行了...
當前是執行緒[Thread-4],處於等待狀態...
當前是執行緒[Thread-5],已經開始運行了...
當前是執行緒[Thread-5],處於等待狀態...
當前是執行緒[Thread-6],已經開始運行了...
當前是執行緒[Thread-6],處於等待狀態...
當前是執行緒[Thread-7],已經開始運行了...
當前是執行緒[Thread-7],處於等待狀態...
當前是執行緒[Thread-8],已經開始運行了...
當前是執行緒[Thread-8],處於等待狀態...
當前是執行緒[Thread-9],已經開始運行了...
當前是執行緒[Thread-9],處於等待狀態...
當前是執行緒[Thread-10],已經開始運行了...
當前是執行緒[Thread-10],處於等待狀態...
請輸入你要執行的執行緒數量:2
當前是執行緒[Thread-1],等待狀態結束,繼續執行...
當前是執行緒[Thread-2],等待狀態結束,繼續執行...
請輸入你要執行的執行緒數量:3
當前是執行緒[Thread-3],等待狀態結束,繼續執行...
當前是執行緒[Thread-5],等待狀態結束,繼續執行...
當前是執行緒[Thread-4],等待狀態結束,繼續執行...
請輸入你要執行的執行緒數量:5
當前是執行緒[Thread-6],等待狀態結束,繼續執行...
當前是執行緒[Thread-7],等待狀態結束,繼續執行...
當前是執行緒[Thread-8],等待狀態結束,繼續執行...
當前是執行緒[Thread-10],等待狀態結束,繼續執行...
當前是執行緒[Thread-9],等待狀態結束,繼續執行...
請輸入你要執行的執行緒數量:1
最終結果: 10
"""

 

上下文管理


 

import threading

num = 0

def task():
    obj = threading.current_thread()
    print("當前是執行緒[{0}],已經開始運行了...".format(obj.getName()))

    with cond:
        global num
        print("當前是執行緒[{0}],處於等待狀態...".format(obj.getName()))
        cond.wait()  # 暫停,等待喚醒
        num += 1
        print("當前是執行緒[{0}],等待狀態結束,繼續執行...".format(obj.getName()))


if __name__ == '__main__':

    cond = threading.Condition()  # 例項化遞迴鎖物件

    for i in range(10):
        t1 = threading.Thread(target=task,)  # 開啟10條執行緒
        t1.start()  # 等待CPU排程執行

    while num < 10:
        task_num = int(input("請輸入你要執行的執行緒數量:"))

        with cond:
            cond.notify(task_num)  # 通知喚醒


    print("最終結果:",num)

# ==== 執行結果 ====

"""
當前是執行緒[Thread-1],已經開始運行了...
當前是執行緒[Thread-1],處於等待狀態...
當前是執行緒[Thread-2],已經開始運行了...
當前是執行緒[Thread-2],處於等待狀態...
當前是執行緒[Thread-3],已經開始運行了...
當前是執行緒[Thread-3],處於等待狀態...
當前是執行緒[Thread-4],已經開始運行了...
當前是執行緒[Thread-4],處於等待狀態...
當前是執行緒[Thread-5],已經開始運行了...
當前是執行緒[Thread-5],處於等待狀態...
當前是執行緒[Thread-6],已經開始運行了...
當前是執行緒[Thread-6],處於等待狀態...
當前是執行緒[Thread-7],已經開始運行了...
當前是執行緒[Thread-7],處於等待狀態...
當前是執行緒[Thread-8],已經開始運行了...
當前是執行緒[Thread-8],處於等待狀態...
當前是執行緒[Thread-9],已經開始運行了...
當前是執行緒[Thread-9],處於等待狀態...
當前是執行緒[Thread-10],已經開始運行了...
當前是執行緒[Thread-10],處於等待狀態...
請輸入你要執行的執行緒數量:2
當前是執行緒[Thread-1],等待狀態結束,繼續執行...
當前是執行緒[Thread-2],等待狀態結束,繼續執行...
請輸入你要執行的執行緒數量:3
當前是執行緒[Thread-3],等待狀態結束,繼續執行...
當前是執行緒[Thread-5],等待狀態結束,繼續執行...
當前是執行緒[Thread-4],等待狀態結束,繼續執行...
請輸入你要執行的執行緒數量:5
當前是執行緒[Thread-6],等待狀態結束,繼續執行...
當前是執行緒[Thread-7],等待狀態結束,繼續執行...
當前是執行緒[Thread-8],等待狀態結束,繼續執行...
當前是執行緒[Thread-10],等待狀態結束,繼續執行...
當前是執行緒[Thread-9],等待狀態結束,繼續執行...
請輸入你要執行的執行緒數量:1
最終結果: 10
"""
with語句的使用

 

Event事件鎖

方法大全


 

Event事件鎖方法方法大全  
方法/屬性名稱 功能描述
is_set() 用來判斷當前紅綠燈(標誌位)的狀態,紅燈為False,綠燈為True
set() 通知所有處於紅燈狀態的執行緒開始執行,這相當於將標誌位改為True
clear() 將所有處於綠燈狀態的執行緒暫停,這相當於將標誌位改為False
wait(timeout=None) 阻塞當前執行緒直到被放行,即等待紅綠燈的通知,紅燈停綠燈行。

 

使用方式


 

  這玩意兒是基於Condition條件鎖做的,跟條件鎖的區別就在於他是非常乾脆利落的。

  注意:事件鎖只能一次全部放行,相當於紅綠燈一樣,等車的紅燈全停,綠燈全過。

  此外,事件鎖不支援上下文管理協議。

 

import threading


def task():
    obj = threading.current_thread()
    print("當前是執行緒[{0}],已經開始運行了...".format(obj.getName()))
    print("當前是執行緒[{0}],紅燈了,停車...".format(obj.getName()))
    event.wait()  # 暫停,等待綠燈通行
    print("當前是執行緒[{0}],綠燈了放行...".format(obj.getName()))
    print("當前是執行緒[{0}],臥槽怎麼又是紅燈,停車...".format(obj.getName()))
    event.wait()  # 暫停,等待綠燈通行
    print("當前是執行緒[{0}],繼續執行...".format(obj.getName()))


if __name__ == '__main__':

    event = threading.Event()  # 例項化事件鎖物件

    for i in range(10):
        t1 = threading.Thread(target=task,)  # 開啟10條執行緒
        t1.start()  # 等待CPU排程執行

    event.set()  # 設定為綠燈 針對第一次
    event.clear()  # 設定為紅燈,如果沒有他那麼上面不管wait()多少次都沒用了。因為全都是綠燈
    event.set()  # 再次設定為綠燈,針對第二次


# ==== 執行結果 ====

"""
當前是執行緒[Thread-1],已經開始運行了...
當前是執行緒[Thread-1],紅燈了,停車...
當前是執行緒[Thread-2],已經開始運行了...
當前是執行緒[Thread-2],紅燈了,停車...
當前是執行緒[Thread-3],已經開始運行了...
當前是執行緒[Thread-3],紅燈了,停車...
當前是執行緒[Thread-4],已經開始運行了...
當前是執行緒[Thread-4],紅燈了,停車...
當前是執行緒[Thread-5],已經開始運行了...
當前是執行緒[Thread-5],紅燈了,停車...
當前是執行緒[Thread-6],已經開始運行了...
當前是執行緒[Thread-6],紅燈了,停車...
當前是執行緒[Thread-7],已經開始運行了...
當前是執行緒[Thread-7],紅燈了,停車...
當前是執行緒[Thread-8],已經開始運行了...
當前是執行緒[Thread-8],紅燈了,停車...
當前是執行緒[Thread-9],已經開始運行了...
當前是執行緒[Thread-9],紅燈了,停車...
當前是執行緒[Thread-10],已經開始運行了...
當前是執行緒[Thread-10],紅燈了,停車...
當前是執行緒[Thread-10],綠燈了放行...
當前是執行緒[Thread-10],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-5],綠燈了放行...
當前是執行緒[Thread-9],綠燈了放行...
當前是執行緒[Thread-9],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-1],綠燈了放行...
當前是執行緒[Thread-1],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-6],綠燈了放行...
當前是執行緒[Thread-6],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-3],綠燈了放行...
當前是執行緒[Thread-4],綠燈了放行...
當前是執行緒[Thread-2],綠燈了放行...
當前是執行緒[Thread-1],繼續執行...
當前是執行緒[Thread-5],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-3],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-7],綠燈了放行...
當前是執行緒[Thread-7],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-7],繼續執行...
當前是執行緒[Thread-4],臥槽怎麼又是紅燈,停車...
當前是執行緒[Thread-3],繼續執行...
當前是執行緒[Thread-10],繼續執行...
當前是執行緒[Thread-5],繼續執行...
當前是執行緒[Thread-8],綠燈了放行...
當前是執行緒[Thread-6],繼續執行...
當前是執行緒[Thread-4],繼續執行...
"""

 

Semaphore訊號量鎖

方法大全


Semaphore訊號量鎖方法方法大全  
方法/屬性名稱 功能描述
acquire(blocking=True, timeout=-1) 上鎖,在上鎖狀態中的程式碼塊執行時不允許切換至其他執行緒執行。
release() 解鎖,解鎖後系統可以切換至其他執行緒執行。

 

使用方式


 

  這玩意兒是可以規定一次最多跑多少執行緒的一個東西。也是基於Condition條件鎖做的。

  注意:區分與Condition條件鎖的區別,Semaphore訊號量鎖不能自由規定,只能規定一次,而條件鎖可以多次規定。

 

import threading
import time

def task():
    sema.acquire()
    time.sleep(1)
    obj = threading.current_thread()
    print("當前是執行緒[{0}],已經開始運行了...".format(obj.getName()))
    sema.release()


if __name__ == '__main__':

    sema = threading.Semaphore(3)  # 例項化訊號量鎖物件,代表每次都跑3條。

    for i in range(10):
        t1 = threading.Thread(target=task,)  # 開啟10條執行緒
        t1.start()  # 等待CPU排程執行



# ==== 執行結果 ====

"""
當前是執行緒[Thread-1],已經開始運行了...
當前是執行緒[Thread-3],已經開始運行了...
當前是執行緒[Thread-2],已經開始運行了...

當前是執行緒[Thread-4],已經開始運行了...
當前是執行緒[Thread-6],已經開始運行了...
當前是執行緒[Thread-5],已經開始運行了...

當前是執行緒[Thread-7],已經開始運行了...
當前是執行緒[Thread-9],已經開始運行了...
當前是執行緒[Thread-8],已經開始運行了...

當前是執行緒[Thread-10],已經開始運行了...
"""

 

上下文管理





import threading
import time

def task():
    with sema:
        time.sleep(1)
        obj = threading.current_thread()
        print("當前是執行緒[{0}],已經開始運行了...".format(obj.getName()))



if __name__ == '__main__':

    sema = threading.Semaphore(3)  # 例項化訊號量鎖物件,代表每次都跑3條。

    for i in range(10):
        t1 = threading.Thread(target=task,)  # 開啟10條執行緒
        t1.start()  # 等待CPU排程執行



# ==== 執行結果 ====

"""
當前是執行緒[Thread-1],已經開始運行了...
當前是執行緒[Thread-3],已經開始運行了...
當前是執行緒[Thread-2],已經開始運行了...

當前是執行緒[Thread-4],已經開始運行了...
當前是執行緒[Thread-6],已經開始運行了...
當前是執行緒[Thread-5],已經開始運行了...

當前是執行緒[Thread-7],已經開始運行了...
當前是執行緒[Thread-9],已經開始運行了...
當前是執行緒[Thread-8],已經開始運行了...

當前是執行緒[Thread-10],已經開始運行了...
"""
with語句的使用

 

擴充套件:練習題

Condition條件鎖的應用


 

  需求:一個空列表,兩個執行緒輪番往裡面加值(一個加偶數,一個加奇數),讓該列表中的值為 1 - 100。

 

import threading
import time

li = []

def even():
    """加偶數"""
    with cond:  # 加鎖
        for i in range(2, 101, 2):
            if len(li) % 2 != 0:
                li.append(i)
                cond.notify()  # notify()並不會立即終止當前執行緒的執行,而是告訴另一執行緒。你可以走了,不過得等我wait()之後
                cond.wait()  # 阻塞住,執行另一執行緒,直到另一執行緒傳送了notify()並且它wait()了之後。
            else:
                cond.wait()
                li.append(i)
                cond.notify()
        else:
            cond.notify()

def odd():
    """加奇數"""
    with cond:
        for i in range(1, 101, 2):
            if len(li) %2 == 0:
                li.append(i)
                cond.notify()
                cond.wait()
        else:
            cond.notify()



if __name__ == '__main__':

    cond = threading.Condition()

    t1 = threading.Thread(target=odd)
    t2 = threading.Thread(target=even)

    t1.start()
    t2.start()
    t1.join()
    t2.join()

    print(li)

 

Event事件鎖的應用


 

  有兩個執行緒,如何讓他們一人一句對答?文字如下:

 

    杜甫:老李啊,來喝酒!

    李白:老杜啊,不喝了我喝不下了!

    杜甫:老李啊,再來一壺?

    杜甫:...老李?

    李白:呼呼呼...睡著了..

 

import threading


def libai():
    event.wait()  
    print("李白:老杜啊,不喝了我喝不下了!")
    event.set()
    event.clear()
    event.wait()
    print("李白:呼呼呼...睡著了..")

def dufu():
    print("杜甫:老李啊,來喝酒!")
    event.set()  
    event.clear()
    event.wait()
    print("杜甫:老李啊,再來一壺?")
    print("杜甫:...老李?")
    event.set()


if __name__ == '__main__':

    event = threading.Event()

    t1 = threading.Thread(target=libai)
    t2 = threading.Thread(target=dufu)

    t1.start()
    t2.start()
    t1.join()
    t2.join()

 

擴充套件:鎖的關係淺析

  這裡我們來聊一聊鎖的關係。

 

  Rlock遞迴鎖,Condition條件鎖,Event事件鎖以及Semaphore訊號量鎖內部都是以同步鎖為基礎的。

 

  RLock遞迴鎖的實現方式非常簡單,因為內部維護著一個計數器。當計數器不為0的時候該執行緒不能被I/O操作和時間輪詢機制切換。但是當計數器為0的時候便不會如此了。

  我們可以看一下遞迴鎖的原始碼:

def __init__(self):
    self._block = _allocate_lock()
    self._owner = None
    self._count = 0  # 計數器

 

  而Condition條件鎖的內部其實是有兩把鎖的,一把底層鎖(同步鎖)一把高階鎖(遞迴鎖)。而低層鎖的解鎖方式有兩種,使用wait()方法會暫時解開底層鎖同時加上一把高階鎖,只有當接收到別的執行緒裡的notfiy()後才會解開高階鎖和重新上鎖低層鎖.

def __init__(self, lock=None):
    if lock is None:
        lock = RLock()  # 可以看到條件鎖的內部是基於遞迴鎖,而遞迴鎖又是基於同步鎖來做的
    self._lock = lock

    self.acquire = lock.acquire
    self.release = lock.release
    try:
        self._release_save = lock._release_save
    except AttributeError:
        pass
    try:
        self._acquire_restore = lock._acquire_restore
    except AttributeError:
        pass
    try:
        self._is_owned = lock._is_owned
    except AttributeError:
        pass
    self._waiters = _deque()

 

  Event事件鎖內部是基於條件鎖來做的。

class Event:

    def __init__(self):
        self._cond = Condition(Lock())  # 例項化出了一個條件鎖。
        self._flag = False

    def _reset_internal_locks(self):
        # private!  called by Thread._reset_internal_locks by _after_fork()
        self._cond.__init__(Lock())

    def is_set(self):
        """Return true if and only if the internal flag is true."""
        return self._flag

    isSet = is_set

 

  Semaphore訊號量鎖內部也是基於條件鎖來做的。

class Semaphore:

    def __init__(self, value=1):
        if value < 0:
            raise ValueError("semaphore initial value must be >= 0")
        self._cond = Condition(Lock()) # 可以看到,這裡是例項化出了一個條件鎖
        self._value = value

&n