目录
- 一、进程同步
- 二、为什么需要进程同步
- 三、Python中实现进程同步
- 四、多进程模拟同时抢票
- 4.1 通过锁控制进程资源访问
- 总结
尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题:当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。
一、进程同步
多个进程同时执行,为了相互制约各进程对资源的访问,使得各个进程的执行相互同步。
在我的理解里,进程同步也算是进程间通讯(ipc)的一种手段。
二、为什么需要进程同步
多进程会引发抢占资源的问题,为了解决这个问题就需要各进程之间相互同步,控制进程对关键资源的访问。
在C语言中实现进程同步的方式有很多,比如:信号量,锁机制等。
例子: 演示多进程资源抢占问题
import os
import time
import random
from multiprocessing import Process
def work(n):
print('%s: %s is running' %(n,os.getpid()))
time.sleep(random.random())
print('%s:%s is done' %(n,os.getpid()))
if __name__ == '__main__':
for i in range(3):
p=Process(target=work,args=(i,))
p.start()
三、Python中实现进程同步
- 首先
from multiprocessing import Lock
- 在主进程中,实例化得到锁,
lock = Lock()
,并传给子进程 - 在子进程中通过上锁和解锁实现对多进程对资源的控制。
lock.acquire()
上锁,lock.release()
解锁
演示:通过Python锁机制控制资源的访问
def work(lock,n):
print(f"等待开锁 进程{n}")
# 上锁
lock.acquire() # 当上锁之后别的进程无法访问,会阻塞住
print(f'进程{n} pid: %s is running' % ( os.getpid()))
time.sleep(random.random())
print(f'进程{n} pid: %s is done' % (os.getpid()))
# 开锁
lock.release()
if __name__ == '__main__':
lock=Lock() # 实例化得到锁
for i in range(3):
p=Process(target=work,args=(lock,i))
p.start()
等待开锁 进程1
进程1 pid: 8696 is running
等待开锁 进程0
等待开锁 进程2
进程1 pid: 8696 is done
进程0 pid: 14264 is running
进程0 pid: 14264 is done
进程2 pid: 22724 is running
进程2 pid: 22724 is done
根据结果可以发现,在上锁之前的代码。多进程是并发访问的,但上锁之后,直到解锁后才能有第二个人访问。以此类推。就好像上厕所排队一样,进去一个人关上门锁上,第二个人等着。
但是看完你会有个疑问,那被锁上的代码,每个进程在访问的时候不就是串行的依次在访问嘛。
确实,锁机制 保证了数据的安全,但牺牲掉效率.
四、多进程模拟同时抢票
# 文件db的内容为:{"count":1}
# 注意一定要用双引号,不然json无法识别
# 并发运行,效率高,但竞争写同一文件,数据写入错乱
from multiprocessing import Process,Lock
import time,json,random
def search():
dic=json.load(open('db'))
print('剩余票数%s' %dic['count'])
def get():
dic=json.load(open('db'))
time.sleep(0.1) # 模拟读数据的网络延迟
if dic['count'] >0:
dic['count']-=1
time.sleep(0.2) # 模拟写数据的网络延迟
json.dump(dic,open('db','w'))
print('购票成功')
def task():
search()
get()
if __name__ == '__main__':
for i in range(100): # 模拟并发100个客户端抢票
p=Process(target=task)
p.start()
上述代码,你会发现多个进程在同时抢票,相互抢占文件资源,每个进程都把文件读入到内存中进行抢票。实际上,资源只能被一个人占有,这就会引发问题
4.1 通过锁控制进程资源访问
def search():
time.sleep(1)
with open("db","w",encoding="utf8") as f:
data = json.load(f)
print(f'还剩{data["count"]}')
def get():
with open('db','rt',encoding='utf-8') as f:
res = json.load(f)
time.sleep(1) # 模拟网络io
if res['count'] > 0:
res['count'] -= 1
with open('db', 'w', encoding='utf-8') as f:
json.dump(res, f)
print(f'进程{os.getpid()} 抢票成功')
time.sleep(1.5) # 模拟网络io
else:
print('票已经售空啦!!!!!!!!!!!')
def task(lock):
print(f"进程:{os.getpid()} 正在抢票中。。。")
# 上锁 每个进程都会访问一遍,所以加锁就等于上锁到解锁这段代码是串行的
lock.acquire() # 当上锁之后别的进程无法访问,会阻塞住
get()
# 开锁
lock.release()
if __name__ == '__main__':
# 创建锁
lock = Lock()
pro_list = []
# 创建进程
for i in range(10):
p = Process(target=task, args=(lock,))
p.start()
pro_list.append(p)
# 回收进程
for i in range(10):
pro_list[i].join()
总结
进程同步理论上应该是进程间通讯中的一部分,是一个大话题,每个语言也都有实现进程同步的需求。需要将各个进程对资源的访问加以限制。在各个语言中也都有限制。这里目前只介绍了这一种进程同步的方法。
锁机制 是把锁住的代码变成了串行,保证了数据的安全,但牺牲掉效率.