并发编程之进程互斥锁
什么是进程同步(互斥锁)
互斥锁(Mutex)是一种用于多线程编程中控制对共享资源访问的机制。
其作用是保证在同一时刻只有一个线程在访问共享资源,从而避免多个线程同时读写数据造成的问题。
互斥锁的基本原理是在对共享资源进行访问前加锁,使得其他线程无法访问该资源,当访问完成后再解锁,使得其他线程可以进行访问。
通过这种方式,可以保证同一时间只有一个线程在执行关键代码段,从而保证了数据的安全性。
需要注意的是,互斥锁会带来一些额外的开销,
进程之间数据不共享,但是共享同一套文件系统,所以访问同一文件,或同一打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理。
例1:多个进程共享同一个打印终端
并发运行效率高,但竞争同一个打印终端,带来了打印错乱
未加锁状态:
import time
from multiprocessing import Process
def task(name):
print('%s 1' % name)
time.sleep(1)
print('%s 2' % name)
time.sleep(1)
print('%s 3' % name)
if __name__ == '__main__':
for i in range(1, 4):
p = Process(target=task, args=('进程%s' % i,))
p.start()
结果:
进程1 1
进程2 1
进程3 1
进程1 2
进程2 2
进程3 2
进程1 3
进程2 3
进程3 3
加锁状态:
由并发变成串行,牺牲了运行效率,但避免了竞争
import time
from multiprocessing import Process, Lock
def task(name, lock):
lock.acquire()
print('%s 1' % name)
time.sleep(1)
print('%s 2' % name)
time.sleep(1)
print('%s 3' % name)
lock.release()
if __name__ == '__main__':
lock = Lock()
for i in range(1, 4):
p = Process(target=task, args=('进程%s' % i, lock))
p.start()
结果:
进程1 1
进程1 2
进程1 3
进程2 1
进程2 2
进程2 3
进程3 1
进程3 2
进程3 3
例2:多个进程共享同一文件
文件当数据库,模拟抢票
并发运行,效率高,但竞争写同一文件,数据写入错乱
未加锁状态:
db.txt文件
{"count": 88}
import json
import time
from multiprocessing import Process
def search(name):
time.sleep(1)
dic = json.load(open('db.txt', 'r', encoding='utf-8'))
print('<%s>查看到剩余票数【%s】' % (name, dic['count']))
def get(name):
time.sleep(1)
dic = json.load(open('db.txt', 'r', encoding='utf-8'))
if dic['count'] > 0:
dic['count'] -= 1
time.sleep(3)
json.dump(dic, open('db.txt', 'w', encoding='utf-8'))
print('<%s>购票成功' % name)
def task(name):
search(name)
get(name)
if __name__ == '__main__':
for i in range(5):
p = Process(target=task, args=('路人%s' % i,))
p.start()
结果:
<路人0>查看到剩余票数【88】
<路人1>查看到剩余票数【88】
<路人2>查看到剩余票数【88】
<路人3>查看到剩余票数【88】
<路人4>查看到剩余票数【88】
<路人0>购票成功
<路人1>购票成功
<路人2>购票成功
<路人3>购票成功
<路人4>购票成功
db.txt文件(执行后文件后,5个人买票成功,剩余票数就减了1),这显然是错误的
{"count": 87}
加锁状态:
购票行为由并发变成了串行,牺牲了运行效率,但保证了数据安全
import json
import time
from multiprocessing import Process, Lock
def search(name):
time.sleep(1)
dic = json.load(open('db.txt', 'r', encoding='utf-8'))
print('<%s>查看到剩余票数【%s】' % (name, dic['count']))
def get(name):
time.sleep(1)
dic = json.load(open('db.txt', 'r', encoding='utf-8'))
if dic['count'] > 0:
dic['count'] -= 1
time.sleep(3)
json.dump(dic, open('db.txt', 'w', encoding='utf-8'))
print('<%s>购票成功' % name)
def task(name, mutex):
search(name)
mutex.acquire()
get(name)
mutex.release()
if __name__ == '__main__':
mutex = Lock()
for i in range(5):
p = Process(target=task, args=('路人%s' % i, mutex))
p.start()
结果:
<路人2>查看到剩余票数【88】
<路人1>查看到剩余票数【88】
<路人0>查看到剩余票数【88】
<路人3>查看到剩余票数【88】
<路人4>查看到剩余票数【88】
<路人2>购票成功
<路人1>购票成功
<路人0>购票成功
<路人3>购票成功
<路人4>购票成功
db.txt文件
{"count": 83}
互斥锁与join的区别
import json
import time
# 加锁:由并发变成串行,牺牲了运行效率,但避免了竞争
from multiprocessing import Process
def search(name):
time.sleep(1)
dic = json.load(open('db.txt', 'r', encoding='utf-8'))
print('<%s> 查看到剩余票数【%s】' % (name, dic['count']))
def get(name):
time.sleep(1)
dic = json.load(open('db.txt', 'r', encoding='utf-8'))
if dic['count'] > 0:
dic['count'] -= 1
time.sleep(3)
json.dump(dic, open('db.txt', 'w', encoding='utf-8'))
print('<%s> 购票成功' % name)
else:
print('<%s> 购票失败' % name)
def task(name, ):
search(name)
# mutex.acquire()
get(name)
# mutex.release()
if __name__ == '__main__':
# mutex=Lock()
for i in range(5):
p = Process(target=task, args=('路人%s' % i,))
p.start()
p.join()
结果:
<路人0> 查看到剩余票数【88】
<路人0> 购票成功
<路人1> 查看到剩余票数【87】
<路人1> 购票成功
<路人2> 查看到剩余票数【86】
<路人2> 购票成功
<路人3> 查看到剩余票数【85】
<路人3> 购票成功
<路人4> 查看到剩余票数【84】
<路人4> 购票成功
db.txt文件
{"count": 83}
事实上,可以多人同时查看剩余票数。
总结:
加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。
虽然可以用文件共享数据实现进程间通信,但问题是:
1、效率低(共享数据基于文件,而文件是硬盘上的数据)
2、需要自己加锁处理
最好找寻一种解决方案能够兼顾:
1、效率高(多个进程共享一块内存的数据)
2、帮我们处理好锁问题。
这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。
下一篇为队列和管道

浙公网安备 33010602011771号