进程锁(互斥锁)
(1)简介
-
进程锁(也称为互斥锁)是一种用于控制多个进程对共享资源访问的机制。在并发编程中,多个进程可能同时访问共享的数据,如果没有适当的同步机制,可能会导致数据不一致或其他问题。进程锁就是用来解决这个问题的一种同步工具。
-
互斥锁的基本思想是,在访问共享资源之前,进程首先必须获得锁。如果锁已经被其他进程获得,那么进程就必须等待,直到锁被释放。这样可以确保同一时刻只有一个进程可以访问共享资源,从而避免了竞态条件和数据不一致的问题。
(2)多个进程共享同一文件
- 文件当数据库,模拟抢票
- 并发运行,数据写入错乱
(1)未加锁示例
import random
from multiprocessing import Process, Lock
import time
import json
import os
# 确定存储车票信息的文件路径
db_path = os.path.join(os.path.dirname(__file__), 'ticket_data.json')
# 初始化车票数据
def init_data():
with open(file=db_path, mode='w', encoding='utf-8') as fp:
json.dump({'ticket_number': 2}, fp)
# 获取车票数据
def get_ticket():
with open(db_path, 'r', encoding='utf8') as f:
ticket_dict = json.load(f)
return ticket_dict
# 保存车票数据
def save_ticket(ticket_dict):
with open(db_path, 'w', encoding='utf8') as f:
json.dump(ticket_dict, f)
# 查询车票
def search_ticket(name):
ticket_dict = get_ticket()
print(f'用户:>>>{name} 正在查询余票:>>>{ticket_dict.get("ticket_number")}')
# 购买车票
def buy_ticket(name):
ticket_dict = get_ticket()
# 模拟购票过程中的随机延迟
time.sleep(random.randint(1, 3))
if ticket_dict.get('ticket_number') > 0:
ticket_dict['ticket_number'] -= 1
save_ticket(ticket_dict)
print(f'用户:>>>{name} 买票成功!!')
else:
print(f'当前无余票!!')
# 主函数,包括查询和购买操作
def main(name):
search_ticket(name)
buy_ticket(name)
if __name__ == '__main__':
# 初始化车票数据
init_data()
# 创建进程列表
p_list = []
# 创建多个进程模拟多用户同时操作
for i in range(1, 5):
p = Process(target=main, args=(i,))
p.start()
p_list.append(p)
# 等待所有进程执行完毕
for p in p_list:
p.join()
# 用户:>>>1 正在查询余票:>>>2
# 用户:>>>4 正在查询余票:>>>2
# 用户:>>>2 正在查询余票:>>>2
# 用户:>>>3 正在查询余票:>>>2
# 用户:>>>1 买票成功!!
# 用户:>>>4 买票成功!!
# 用户:>>>3 买票成功!!
# 用户:>>>2 买票成功!!
(2)加锁
- 针对上述数据错乱问题,解决方式就是加锁处理
- 将并发变成串行牺牲效率,但是保证了数据的安全
import random
from multiprocessing import Process, Lock
import time
import json
import os
# 确定存储车票信息的文件路径
db_path = os.path.join(os.path.dirname(__file__), 'ticket_data.json')
# 初始化车票数据
def init_data():
with open(file=db_path, mode='w', encoding='utf-8') as fp:
json.dump({'ticket_number': 2}, fp)
# 获取车票数据
def get_ticket():
with open(db_path, 'r', encoding='utf8') as f:
ticket_dict = json.load(f)
return ticket_dict
# 保存车票数据
def save_ticket(ticket_dict):
with open(db_path, 'w', encoding='utf8') as f:
json.dump(ticket_dict, f)
# 查询车票
def search_ticket(name):
ticket_dict = get_ticket()
print(f'用户:>>>{name} 正在查询余票:>>>{ticket_dict.get("ticket_number")}')
# 购买车票
def buy_ticket(name, mutex):
ticket_dict = get_ticket()
time.sleep(random.randint(1, 3))
if ticket_dict.get('ticket_number') > 0:
ticket_dict['ticket_number'] -= 1
save_ticket(ticket_dict)
print(f'用户:>>>{name} 买票成功!!')
else:
print(f'当前无余票!!')
# 主函数,包括查询和购买操作
def main(name, mutex):
search_ticket(name)
# 请求互斥锁
mutex.acquire()
try:
buy_ticket(name, mutex)
finally:
# 释放互斥锁
mutex.release()
if __name__ == '__main__':
# 初始化车票数据
init_data()
# 创建互斥锁
mutex = Lock()
# 创建进程列表
p_list = []
# 创建多个进程模拟多用户同时购票操作
for i in range(1, 5):
p = Process(target=main, args=(i, mutex))
p.start()
p_list.append(p)
# 等待所有进程执行完毕
for p in p_list:
p.join()
# 所有人都能查票成功,但是只有前两名购票成功
# 前两名是随机抢到的,不一定是按顺序(考虑到各种因素,如网络等)
# 用户:>>>1 正在查询余票:>>>2
# 用户:>>>4 正在查询余票:>>>2
# 用户:>>>3 正在查询余票:>>>2
# 用户:>>>2 正在查询余票:>>>2
# 用户:>>>1 买票成功!!
# 用户:>>>4 买票成功!!
# 当前无余票!!
# 当前无余票!!
mutex = Lock()
: 创建了一个互斥锁对象。mutex.acquire()
: 请求互斥锁,如果锁已经被其他进程占用,会阻塞等待。try
中的代码块:在互斥锁保护下执行购票操作。finally
中的代码块:无论购票操作是否成功,都会释放互斥锁,确保其他进程可以访问共享资源。- 进程列表
p_list
:存储了所有创建的进程对象,用于后续等待所有进程执行完毕。