互斥锁
1.互斥锁的概念
互斥锁: 对共享数据进行锁定,保证同一时刻只能有一个线程去操作。
注意:
互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程需要等待,等互斥锁使用完释放后,其它等待的线程再去抢这个锁。
2.互斥锁的使用
threading模块中定义了Lock变量,这个变量本质上是一个函数,通过调用这个函数可以获取一把互斥锁。
互斥锁使用步骤:
# 创建锁 mutex = threading.Lock() # 上锁 mutex.acquire() ...这里编写代码能保证同一时刻只能有一个线程去操作, 对共享数据进行锁定... # 释放锁 mutex.release()
注意点:
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() # 提示:加上互斥锁,那个线程抢到这个锁我们决定不了,那线程抢到锁那个线程先执行,没有抢到的线程需要等待 # 加上互斥锁多任务瞬间变成单任务,性能会下降,也就是说同一时刻只能有一个线程去执行
执行结果:
sum1: 1000000 sum2: 2000000
说明:
通过执行结果可以地址互斥锁能够保证多个线程访问共享数据不会出现数据错误问题,如果不用线程的话会出现加不到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()
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()
4. 死锁小总结
使用互斥锁的时候需要注意死锁的问题,要在合适的地方注意释放锁。
死锁一旦产生就会造成应用程序的停止响应,应用程序无法再继续往下执行了。