Linux中提供一把互斥鎖mutex(也稱之為互斥量)。
每個線程在對資源操作前都嘗試先加鎖,成功加鎖才能操作,操作結束解鎖。
但通過“鎖”就將資源的訪問變成互斥操作,而后與時間有關的錯誤也不會再產生了。
但,應注意:同一時刻,只能有一個線程持有該鎖。
當A線程對某個全局變量加鎖訪問,B在訪問前嘗試加鎖,拿不到鎖,B阻塞。C線程不去加鎖,而直接訪問該全局變量,依然能夠訪問,但會出現數據混亂。
所以,互斥鎖實質上是操作系統提供的一把“建議鎖”(又稱“協同鎖”),建議程序中有多線程訪問共享資源的時候使用該機制。但,並沒有強制限定。
因此,即使有了mutex,如果有線程不按規則來訪問數據,依然會造成數據混亂。
原文鏈接:https://blog.csdn.net/qq_39736982/article/details/82348672
互斥鎖
1.互斥鎖的概念
互斥鎖: 對共享數據進行鎖定,保證同一時刻只能有一個線程去操作。
注意:
互斥鎖是多個線程一起去搶,搶到鎖的線程先執行,沒有搶到鎖的線程需要等待,等互斥鎖使用完釋放后,其它等待的線程再去搶這個鎖。
2.互斥鎖的使用
threading模塊中定義了Lock變量,這個變量本質上是一個函數,通過調用這個函數可以獲取一把互斥鎖。
互斥鎖使用步驟:
# 創建鎖
mutex = threading.Lock()
# 上鎖
mutex.acquire()
...這里編寫代碼能保證同一時刻只能有一個線程去操作, 對共享數據進行鎖定...
# 釋放鎖
mutex.release()
1
2
3
4
5
6
7
8
9
10
注意點:
acquire和release方法之間的代碼同一時刻只能有一個線程去操作
如果在調用acquire方法的時候 其他線程已經使用了這個互斥鎖,那么此時acquire方法會堵塞,直到這個互斥鎖釋放后才能再次上鎖。
3. 測試:使用互斥鎖完成2個線程對同一個全局變量各加100萬次的操作
import threading
# 定義全局變量
g_num = 0
# 創建全局互斥鎖
lock = threading.Lock()
# 循環一次給全局變量加1
def sum_num1():
# 上鎖
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sum1:", g_num)
# 釋放鎖
lock.release()
# 循環一次給全局變量加1
def sum_num2():
# 上鎖
lock.acquire()
for i in range(1000000):
global g_num
g_num += 1
print("sum2:", g_num)
# 釋放鎖
lock.release()
if __name__ == '__main__':
# 創建兩個線程
first_thread = threading.Thread(target=sum_num1)
second_thread = threading.Thread(target=sum_num2)
# 啟動線程
first_thread.start()
second_thread.start()
# 提示:加上互斥鎖,那個線程搶到這個鎖我們決定不了,那線程搶到鎖那個線程先執行,沒有搶到的線程需要等待
# 加上互斥鎖多任務瞬間變成單任務,性能會下降,也就是說同一時刻只能有一個線程去執行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
執行結果:
sum1: 1000000
sum2: 2000000
1
2
說明:
通過執行結果可以地址互斥鎖能夠保證多個線程訪問共享數據不會出現數據錯誤問題,如果不用線程的話會出現加不到2000000的情況。
4.互斥鎖小總結
互斥鎖的作用就是保證同一時刻只能有一個線程去操作共享數據,保證共享數據不會出現錯誤問題
使用互斥鎖的好處確保某段關鍵代碼只能由一個線程從頭到尾完整地去執行
使用互斥鎖會影響代碼的執行效率,多任務改成了單任務執行
互斥鎖如果沒有使用好容易出現死鎖的情況
死鎖
1. 死鎖的概念
死鎖: 一直等待對方釋放鎖的情景就是死鎖
死鎖的結果
會造成應用程序的停止響應,不能再處理其它任務了。
2. 死鎖示例
需求:
根據下標在列表中取值, 保證同一時刻只能有一個線程去取值
import threading
import time
# 創建互斥鎖
lock = threading.Lock()
# 根據下標去取值, 保證同一時刻只能有一個線程去取值
def get_value(index):
# 上鎖
lock.acquire()
print(threading.current_thread())
my_list = [3,6,8,1]
# 判斷下標釋放越界
if index >= len(my_list):
print("下標越界:", index)
return
value = my_list[index]
print(value)
time.sleep(0.2)
# 釋放鎖
lock.release()
if __name__ == '__main__':
# 模擬大量線程去執行取值操作
for i in range(30):
sub_thread = threading.Thread(target=get_value, args=(i,))
sub_thread.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
3. 避免死鎖
在合適的地方釋放鎖
import threading
import time
# 創建互斥鎖
lock = threading.Lock()
# 根據下標去取值, 保證同一時刻只能有一個線程去取值
def get_value(index):
# 上鎖
lock.acquire()
print(threading.current_thread())
my_list = [3,6,8,1]
if index >= len(my_list):
print("下標越界:", index)
# 當下標越界需要釋放鎖,讓后面的線程還可以取值
lock.release()
return
value = my_list[index]
print(value)
time.sleep(0.2)
# 釋放鎖
lock.release()
if __name__ == '__main__':
# 模擬大量線程去執行取值操作
for i in range(30):
sub_thread = threading.Thread(target=get_value, args=(i,))
sub_thread.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
4. 死鎖小總結
使用互斥鎖的時候需要注意死鎖的問題,要在合適的地方注意釋放鎖。
死鎖一旦產生就會造成應用程序的停止響應,應用程序無法再繼續往下執行了。
原文鏈接:https://blog.csdn.net/weixin_45525272/article/details/109133993