在 Python 中多线程中为了防止资源竞争而出现问题,提供了锁的机制,当一个线程操作资源时进行加锁,操作完毕后释放锁,这样其他线程就不会同时操作资源导出出现异常。
在 Python 多线程中注意是两种锁:互斥锁和递归锁
那么它们有什么区别呢?
互斥锁:
一旦一个线程获得一个互斥锁,会阻塞随后尝试获得锁的线程,直到它被释放;任何线程都可以释放它。
递归锁:
递归锁必须由获取它的线程释放。一旦线程获得了递归锁,同一个线程再次获取它将不阻塞;线程必须在每次获取它时释放一次。
虽然锁可以防止程序出问题,但是使用锁不得当是很容易出现死锁。
死锁 : 当线程A持有独占锁a,并尝试去获取独占锁b的同时,线程B持有独占锁b,并尝试获取独占锁a的情况下,就会发生AB两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。通俗的将就是一种相互等待无法结束的现象。
下面就演示一下在 Python 中可能出现的死锁
import threading
import time
num = 0
# 注意:在普通的互斥锁中,在同一个线程内如果存在一个函数多次获得锁,就会出现死锁。注意:递归锁就不会。
llock = threading.Lock() # 创建一个互斥锁
def add_num(lock):
global num
lock.acquire()
num += 1
lock.release()
def run1(n, lock):
global num
print('task ',n)
time.sleep(1)
lock.acquire()
for i in range(1000000):
# lock.acquire()
add_num(lock)
# lock.release()
lock.release()
print(num)
def run2(n, lock):
global num
print('task ',n)
time.sleep(1)
lock.acquire()
for i in range(1000000):
# lock.acquire()
add_num(lock)
# lock.release()
lock.release()
print(num)
if __name__ == '__main__':
start_time = time.time()
# 定义多线程 如果我们传入的是互斥锁,那么就会出现死锁,
t1 = threading.Thread(target=run1, args=('线程A', llock))
t2 = threading.Thread(target=run2, args=('线程B', llock))
# 启动多线程
t1.start()
t2.start()
# 设置主线程等待子线程完成后退出,设置完后主线程会阻塞在这里
t1.join()
t2.join()
print('消耗时间:', time.time() - start_time)
import threading
import time
'''
互相等待的死锁:
当锁出现互相等待的时候就会出现死锁。例如方法1 先将a加锁,并且想获取b的锁,然而此时方法2先将b加锁,再将b加锁。由于方法1没有得到b的锁因此不会释放。导致方法2无法获取b的锁。出现死锁。
run1(a, b)
acquire(a) 将a加锁
acquire(b) 将b加锁
run2(, b)
acquire(b) 将b加锁,前提是其他方法先释放b的锁
acquire(a)
'''
a = 0
b = 0
# 创建一个互斥锁,就算使用递归锁,这种相互等待的也会出现死锁
lockA = threading.Lock()
lockB = threading.Lock()
def run1(n):
global a
global b
print('task ',n)
lockA.acquire()
a += 1
time.sleep(1)
lockB.acquire()
b += 1
lockB.release()
lockA.release()
print(a)
def run2(n):
global a
global b
print('task ',n)
lockB.acquire()
b += 1
lockA.acquire()
a += 1
lockA.release()
lockB.release()
print(a)
if __name__ == '__main__':
start_time = time.time()
# 定义多线程
t1 = threading.Thread(target=run1, args=('线程A', ))
t2 = threading.Thread(target=run2, args=('线程B', ))
# 启动多线程
t1.start()
t2.start()
# 设置主线程等待子线程完成后退出,设置完后主线程会阻塞在这里
t1.join()
t2.join()
print('消耗时间:', time.time() - start_time)
import threading
import time
num = 0
# 创建一个递归锁 在同一个线程里面可以连续调用多次acquire()获得锁,但是要保证调用acquire的次数和release的次数保持一致。
# 注意:在普通的互斥锁中,在同一个线程内如果存在一个函数多次获得锁,就会出现死锁。递归锁就不会。下面具体例子说明
rlock = threading.RLock() # 创建一个递归锁
llock = threading.Lock() # 创建一个互斥锁
def add_num(lock):
global num
lock.acquire()
num += 1
lock.release()
def run1(n, lock):
global num
print('task ',n)
time.sleep(1)
lock.acquire()
for i in range(1000000):
# lock.acquire()
add_num(lock)
# lock.release()
lock.release()
print(num)
def run2(n, lock):
global num
print('task ',n)
time.sleep(1)
lock.acquire()
for i in range(1000000):
# lock.acquire()
add_num(lock)
# lock.release()
lock.release()
print(num)
if __name__ == '__main__':
start_time = time.time()
# 定义多线程 如果我们传入的是互斥锁,那么就会出现死锁,
# t1 = threading.Thread(target=run1, args=('线程A', llock))
# t2 = threading.Thread(target=run2, args=('线程B', llock))
# 传入递归锁
t1 = threading.Thread(target=run1, args=('线程A', rlock))
t2 = threading.Thread(target=run2, args=('线程B', rlock))
# 启动多线程
t1.start()
t2.start()
# 设置主线程等待子线程完成后退出,设置完后主线程会阻塞在这里
t1.join()
t2.join()
print('消耗时间:', time.time() - start_time)