并发编程—互斥锁
1、 互斥锁与递归锁
1.1 模块
普通锁:
线程:from threading import Lock
进程:from multiprocessing import Lock
递归锁:
线程:from threading import RLock
进程:from multiprocessing import RLock
1.2 使用
普通锁:
a = Lock()
b = Lock()
a.aqure() # 加锁
pass # 被锁代码
a.release() # 释放锁
b.aqure()
pass
b.release()
递归锁:
a = b = RLock()
a.aqure()
pass
a.release()
b.aqure()
pass
b.release()
2、进程互斥锁
3、线程互斥锁
3.1 线程不安全
线程—生产者与消费者中num_list是全局变量,受到GIL锁的保护,所以并没有出现数据紊乱
如果num_list不是全局变量,那么会出现数据紊乱现象
如:将数据存在文件中
import time
import random
from concurrent.futures import ThreadPoolExecutor
p = ThreadPoolExecutor(10)
def get_num_list(i):
with open('data_file') as fr:
fr.seek(0)
data = fr.read()
if data.count('[') != 1 or data.count(']') != 1:
print(f'{i}发现数据保存紊乱')
time.sleep(2)
return
num_list = eval(data)
return num_list
def save_num_list(num_list):
with open('data_file', 'w') as fw:
fw.write(str(num_list))
time.sleep(0.1)
# 生产者任务
def create_data(i):
while True:
num_list = get_num_list(i)
print(f'num_list:{num_list}')
if len(num_list) >= 10:
continue
num = random.random()
num_list.append(num)
save_num_list(num_list)
print(f'{i}生产了数字:{num}')
# 消费者任务
def get_data(i):
while True:
num_list = get_num_list(i)
if not len(num_list):
continue
num = num_list[-1]
del num_list[-1]
save_num_list(num_list)
print(f'{i}消耗掉了数字:{num}')
# 创建多对生产者与多消费者
for i in range(5):
p.submit(create_data, i)
p.submit(get_data, i)
可以看到,经常会出现数据紊乱
产生数据紊乱的原因:
1、对同一任务creat_data或者get_data而言,可以被多个线程异步访问
2、对共享数据data_file而言,消费者和生产者能异步访问
上述两种原因,造成了是一个现象:所有的生产者和消费者都时异步访问data_file,都能对它查询以及修改
要解决这个问题,需要解决两个问题,实现:
1、针对同一任务create_data或者get_data,只能线程同步访问:保证生产者消费者互不干扰
2、针对共享数据data_file,只能线程同步访问:保证数据安全
实现:
将获取列表函数与保存列表函数分别加锁,但是这样不能保证生产者消费者互不干扰,有可能A在获取列表,B在保存列表,那么B保存完之后,A拿到的数据就不是真正的数据了。解决办法需要用到别的技术,比如队列
注意:此例使用两把不同的锁,会有问题,暂时没有查出原因,使用递归锁RLock
可以实现
4、死锁现象
锁套着锁,分别拿到一把锁,此时都不能解锁,即死锁
def task1():
a.aqure()
pass
b.aqure()
pass
b.release()
pass
a.release()
def task2():
b.aqure()
pass
a.aqure()
pass
a.release()
pass
b.release()
5、GIL全局解释器锁
5.1 什么是GIL
GIL的全称是 Global Interpreter Lock,全局解释器锁
python的执行依赖于解释器,解释器基于不同编程语言有不同的版本
基于C/C++语言编写的python解释器为Cpython,设计时,为了保证多线程之间数据完整性与状态同步,设计为在任意时刻同一个进程内只有一个线程可以访问该进程内的共享数据,由GIL来控制实现
5.2 GIL与互斥锁
GIL本质上也是互斥锁,保护的是解释器级别的数据,或者说保护的是进程内的共享数据(全局变量)
注意点:
1、GIL是解释器自动实现,互斥锁Lock需要开发者自行添加
2、当申请到GIL全局解释器锁的进程进入阻塞态(即遇到IO操作),GIL会立即失效,别的进程就可以申请GIL
3、GIL不会保护用户自己的数据,需要开发者自己添加互斥锁