日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

python线程同步锁_[python] 线程间同步之Lock RLock

發布時間:2025/3/11 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python线程同步锁_[python] 线程间同步之Lock RLock 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么需要同步

同樣舉之前的例子,兩個線程分別對同一個全局變量進行加減,得不到預期結果,代碼如下:

total = 0

def add():

global total

for i in range(1000000):

total += 1

def desc():

global total

for i in range(1000000):

total -= 1

import threading

thread1 = threading.Thread(target=add)

thread2 = threading.Thread(target=desc)

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print(total)

原因就是因為 += 和 -=并不是原子操作。可以使用dis模塊查看字節碼:

import dis

def add(total):

total += 1

def desc(total):

total -= 1

total = 0

print(dis.dis(add))

print(dis.dis(desc))

# 運行結果如下:

# 3 0 LOAD_FAST 0 (total)

# 3 LOAD_CONST 1 (1)

# 6 INPLACE_ADD

# 7 STORE_FAST 0 (total)

# 10 LOAD_CONST 0 (None)

# 13 RETURN_VALUE

# None

# 5 0 LOAD_FAST 0 (total)

# 3 LOAD_CONST 1 (1)

# 6 INPLACE_SUBTRACT

# 7 STORE_FAST 0 (total)

# 10 LOAD_CONST 0 (None)

# 13 RETURN_VALUE

# None

可以看到 add()函數雖然其中只有一行代碼,但是字節碼主要分為四個步驟:

load 變量total

load 常量 1

執行加法操作

對total進行賦值

同理,desc()函數的步驟相同,只是第三步改為執行減法。

假設一種極端情況,開始total = 0,首先線程1 load 變量total,得到值為0,切換到線程2,同樣的到total為0,再次切換線程1 load常量1,執行加法,給total賦值得到1;然后線程2也 laod常量1,執行減法,給total賦值為-1。最終total為-1,而不是預期的0。

期望中,必須在+=操作結束后,才能執行-=,所以線程同步的需求就出來了。

互斥鎖Lock

threading模塊中提供了threading.Lock類(互斥鎖),基本用法如下:

import threading

lock = threading.Lock()

lock.acquire() # 獲取鎖

# dosomething…… # 臨界區的代碼只能被同時只能被一個線程運行

lock.release() # 釋放鎖

將上面的代碼修改,即可得到正確結果:

import threading

total = 0

lock = threading.Lock()

def add(lock):

global total

for i in range(1000000):

lock.acquire()

total += 1

lock.release()

def desc(lock):

global total

for i in range(1000000):

lock.acquire()

total -= 1

lock.release()

thread1 = threading.Thread(target=add, args=(lock,))

thread2 = threading.Thread(target=desc, args=(lock,))

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print(total)

# 執行結果 0

可以看到,添加互斥鎖之后,程序執行結果是正確的,但是用了互斥鎖之后,同樣有一些缺陷:

添加鎖之后,會影響程序性能。

可能引起"死鎖"。

死鎖主要在兩種情況下發生:

迭代死鎖

一個線程“迭代”請求同一個資源 ,會造成死鎖。

lock = threading.Lock()

lock.acquire()

lock.acquire()

total += 1

lock.release()

lock.release()

上例種,第一次請求資源后還未 release ,再次acquire,最終無法釋放,造成死鎖 。(可通過可重入鎖解決這個問題)。

互相調用死鎖

兩個線程中都會調用相同的資源,互相等待對方結束的情況 。假設A線程需要資源a,b,B線程也需要資源a,b,而線程A先獲取資源a后,再獲取資源b, B線程先獲取資源b,再獲取資源a。在A線程獲取資源a,B線程獲取資源b后,A線程在等待B線程釋放資源b,而B線程在等待A線程釋放資源a,從而死鎖就發生了。

import threading

import time

lock_a = threading.Lock()

lock_b = threading.Lock()

def func1():

global lock_a

global lock_b

lock_a.acquire()

time.sleep(1)

lock_b.acquire()

time.sleep(1)

lock_b.release()

lock_a.release()

def func2():

global lock_a

global lock_b

lock_b.acquire()

time.sleep(1)

lock_a.acquire()

time.sleep(1)

lock_a.release()

lock_b.release()

thread1 = threading.Thread(target=func1)

thread2 = threading.Thread(target=func2)

thread1.start()

thread2.start()

thread1.join()

thread2.join()

print("program finished")

# 程序會陷入死循環

這個例子比較重要,開始理解錯了,如果B線程獲取了資源b,然后釋放之后再獲取資源a,這樣是不會發生死鎖的。只有在B線程獲取了資源b,還沒有釋放的時候,獲取了資源a,才會發生死鎖。

可重入鎖RLock

為解決同一線程種不能多次請求同一資源的問題,python提供了“可重入鎖”:threading.RLock,RLock內部維護著一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源可以被多次require。直到一個線程所有的acquire都被release,其他的線程才能獲得資源 。用法和threading.Lock類相同。

將上面迭代死鎖的代碼改寫一下,就不會發生死鎖,但注意,調用acquire和release的次數必須相等。

lock = threading.RLock()

lock.acquire()

lock.acquire()

total += 1

lock.release()

lock.release()

一般不會寫這么無聊的代碼,但是有一種情況是可能發生的,在加鎖區域調用了某個函數,而這個函數內部又申請了同樣的資源。

lock = threading.RLock()

def dosomething(lock):

lock.acquire()

# do something

lock.release()

lock.acquire()

dosomething(lock)

lock.release()

總結

線程間訪問同一變量需要同步。

線程間加鎖會導致性能損失。

加鎖可能產生死鎖,迭代死鎖和互相調用死鎖。

可重入鎖可以避免迭代死鎖。

參考

總結

以上是生活随笔為你收集整理的python线程同步锁_[python] 线程间同步之Lock RLock的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。