python3多執行緒----鎖機制
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Version: python 3.6.3
# Tools: Pycharm 2017.3.3
__date__ = '2018/7/20 9:49'
__author__ = 'cdl'
import time
import threading
"""執行緒中的鎖機制"""
"""
有兩個執行緒A和B,A和B裡的程式都加了同一個鎖物件,當執行緒A率先執行到lock.acquire()(拿到全域性唯一的鎖後).
執行緒B只能等到執行緒A釋放鎖lock.release()後(歸還鎖)才能執行lock.acquire()(拿到全域性唯一的鎖)並執行後面的程式碼
"""
# 使用鎖
lock = threading.Lock() # 生成鎖物件,全域性唯一
lock.acquire() # 獲取鎖。未獲取到的執行緒會阻塞程式,直到獲取到鎖才會往下執行
lock.release() # 釋放鎖,歸回鎖,其他執行緒可以拿去用了
# 注:lock.acquire() 和 lock.release()必須成對出現。否則就有可能造成死鎖
# 為了避免出現死鎖情況,推薦使用上下文管理器來加鎖
lock = threading.Lock()
with lock: # with語句會在這個程式碼塊執行前自動獲取鎖,在執行結束後自動釋放鎖
# 這裡寫自己的程式碼
pass
# 使用鎖的意義? -----------加鎖是為了對鎖內資源(變數)進行鎖定,避免其他執行緒篡改已被鎖定的資源,以達到我們預期的效果。
# 對比不用鎖的執行緒
# def job1():
# global n
# for i in range(10):
# n += 1
# print('job1', n)
# def job2():
# global n
# for i in range(20):
# n += 10
# print('job2', n)
#
# n = 0
# threading_01 = threading.Thread(target=job1)
# threading_02 = threading.Thread(target=job2)
# threading_01.start()
# threading_02.start()
# 加鎖之後進行對比
def job1():
global n, lock
lock.acquire() # 獲取鎖
for i in range(10):
n += 1
print('job1', n)
lock.release()
def job2():
global n, lock
lock.acquire() # 獲取鎖
for i in range(10):
n += 10
print('job2', n)
lock.release()
n = 0
lock = threading.Lock() # 生成鎖物件,全域性唯一
threading_01 = threading.Thread(target=job1)
threading_02 = threading.Thread(target=job2)
threading_01.start()
threading_02.start()
# 有時候在同一個執行緒中,我們可能會多次請求同一資源(就是,獲取同一鎖鑰匙),俗稱鎖巢狀
def main():
n = 0
lock = threading.Lock()
with lock:
for i in range(10):
n += 1
with lock: # 第二次獲取鎖時,發現鎖已經被同一執行緒的人拿走了。自己也就理所當然,拿不到鎖,程式就卡住了。
print(n)
t1 = threading.Thread(target=main)
t1.start()
# 可重入鎖RLock,專門來處理這個問題
def num():
n = 0
# 生成可重入鎖物件
lock = threading.RLock()
with lock:
for i in range(10):
n += 1
with lock:
print(n)
t1 = threading.Thread(target=num)
t1.start()
# 防止死鎖的加鎖機制
"""
出現情況:
同一執行緒,巢狀獲取同把鎖,造成死鎖
多個執行緒,不按順序同時獲取多個鎖。造成死鎖
具體解釋:
執行緒1,巢狀獲取A,B兩個鎖,執行緒2,巢狀獲取B,A兩個鎖。
由於兩個執行緒是交替執行的,是有機會遇到執行緒1獲取到鎖A,而未獲取到鎖B,在同一時刻,執行緒2獲取到鎖B,而未獲取到鎖A。
由於鎖B已經被執行緒2獲取了,所以執行緒1就卡在了獲取鎖B處,由於是巢狀鎖,執行緒1未獲取並釋放B,是不能釋放鎖A的。
這是導致執行緒2也獲取不到鎖A,也卡住了。兩個執行緒,各執一鎖,各不讓步,造成死鎖。
"""
import threading
from contextlib import contextmanager
# Thread-local state to stored information on locks already acquired
_local = threading.local()
@contextmanager
def acquire(*locks):
# Sort locks by object identifier
locks = sorted(locks, key=lambda x: id(x))
# Make sure lock order of previously acquired locks is not violated
acquired = getattr(_local,'acquired',[])
if acquired and max(id(lock) for lock in acquired) >= id(locks[0]):
raise RuntimeError('Lock Order Violation')
# Acquire all of the locks
acquired.extend(locks)
_local.acquired = acquired
try:
for lock in locks:
lock.acquire()
yield
finally:
# Release locks in reverse order of acquisition
for lock in reversed(locks):
lock.release()
del acquired[-len(locks):]
x_lock = threading.Lock()
y_lock = threading.Lock()
def thread_1():
while True:
with acquire(x_lock):
with acquire(y_lock):
print('Thread-1')
def thread_2():
while True:
with acquire(y_lock):
with acquire(x_lock):
print('Thread-2')
t1 = threading.Thread(target=thread_1)
t1.daemon = True
t1.start()
t2 = threading.Thread(target=thread_2)
t2.daemon = True
t2.start()