进程锁(互斥锁)

进程锁(互斥锁)

(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:存储了所有创建的进程对象,用于后续等待所有进程执行完毕。
posted @ 2024-01-17 16:50  ssrheart  阅读(48)  评论(0编辑  收藏  举报