【一】互斥锁
-
互斥锁(Mutex,全称 Mutual Exclusion Lock)是一种用于控制多个线程对共享资源访问的同步机制。它的核心概念是确保同一时间只有一个线程可以访问某个特定的资源或代码块。互斥锁可以避免资源的并发访问造成的数据冲突和不一致的问题。
-
使用互斥锁时,通常遵循以下步骤:
- 锁定(Locking):当线程需要访问共享资源时,它首先尝试锁定互斥锁。
- 使用资源:如果互斥锁未被其他线程占用,该线程锁定互斥锁并访问资源。
- 解锁(Unlocking):访问完成后,线程释放(解锁)互斥锁,使得其他线程可以访问该资源。
- 以12306买票的例子来说明互斥锁的作用: 假设有一个火车只剩下2张票,现在有多个人(进程)同时在12306上尝试购买这张票。
【二】没有互斥锁的情况
- 如果没有互斥锁,可能会出现多个人同时看到票还在,于是他们都尝试购买。这可能导致超卖的情况,即系统错误地认为有多张票被卖出。
# 模拟抢票过程
from multiprocessing import Process
import time
import json
import random
# 查票
def search_ticket(name):
# 读取文件,查询车票数量
with open('data/tickets', 'r', encoding='utf-8') as fp:
dic = json.load(fp)
tickets_num = dic.get("tickets_num")
print(f'用户{name}查询余票:>>>>{tickets_num}')
return tickets_num, dic
def save_data(dic):
with open('data/tickets', 'w', encoding='utf-8') as fp:
json.dump(dic, fp, ensure_ascii=False)
def buy_tickets(name):
tickets_num, dic = search_ticket(name=name)
time.sleep(random.randint(1, 3)) # 模拟随机延迟
if tickets_num > 0:
tickets_num -= 1
dic["tickets_num"] = tickets_num
save_data(dic=dic)
print(f'用户{name}买票成功')
else:
print(f'用户{name}买票失败')
def process(name):
search_ticket(name=name)
buy_tickets(name=name)
if __name__ == '__main__':
for i in range(5):
p = Process(target=process, args=(i,))
p.start()
'''
用户0查询余票:>>>>2
用户1查询余票:>>>>2
用户0查询余票:>>>>2
用户1查询余票:>>>>2
用户2查询余票:>>>>2
用户2查询余票:>>>>2
用户3查询余票:>>>>2
用户3查询余票:>>>>2
用户4查询余票:>>>>2
用户4查询余票:>>>>2
用户0买票成功
用户1买票成功
用户4买票成功
用户2买票成功
用户3买票成功
进程已结束,退出代码0
'''
【三】有互斥锁的情况
- 使用互斥锁后,当一个人尝试购买这张票时,系统会锁定这个操作。这时,其他人虽然也在尝试购买,但由于互斥锁已经被占用,他们无法同时进行购票操作。只有当第一个人完成购买(无论成功与否)并释放了互斥锁,其他人才能继续尝试购买。这就确保了不会发生超卖的情况。
# 有互斥锁的情况
# 模拟抢票过程
from multiprocessing import Process, Lock
import time
import json
import random
# 查票
def search_ticket(name):
# 读取文件,查询车票数量
with open('data/tickets', 'r', encoding='utf-8') as fp:
dic = json.load(fp)
tickets_num = dic.get("tickets_num")
print(f'用户{name}查询余票:>>>>{tickets_num}')
return tickets_num, dic
def save_data(dic):
with open('data/tickets', 'w', encoding='utf-8') as fp:
json.dump(dic, fp, ensure_ascii=False)
def buy_tickets(name):
tickets_num, dic = search_ticket(name=name)
time.sleep(random.randint(1, 3)) # 模拟随机延迟
if tickets_num > 0:
tickets_num -= 1
dic["tickets_num"] = tickets_num
save_data(dic=dic)
print(f'用户{name}买票成功')
else:
print(f'用户{name}买票失败')
def process(name, mutex):
search_ticket(name=name)
# 抢锁
mutex.acquire()
buy_tickets(name=name)
# 释放锁
mutex.release()
if __name__ == '__main__':
mutex = Lock()
for i in range(5):
p = Process(target=process, args=(i, mutex))
p.start()
'''
用户1查询余票:>>>>2
用户1查询余票:>>>>2
用户0查询余票:>>>>2
用户2查询余票:>>>>2
用户3查询余票:>>>>2
用户4查询余票:>>>>2
用户1买票成功
用户0查询余票:>>>>1
用户0买票成功
用户2查询余票:>>>>0
用户2买票失败
用户3查询余票:>>>>0
用户3买票失败
用户4查询余票:>>>>0
用户4买票失败
进程已结束,退出代码0
'''
【四】注意
- 不要轻易加锁,加锁只应该在争夺数据的环节加