进程同步multiprocess.Lock

我们千方百计实现了程序的异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题:当多个进程使用同一份数据资源的时候,就会引发数据安全或顺序混乱问题。

一、多进程抢占输出资源

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()

0: 46160 is done

1: 56236 is running

0: 46160 is running

2: 53824 is running

1: 56236 is done

2: 53824 is done

二、使用锁维护执行顺序

import os
import time
import random
from multiprocessing import Process,Lock def work(lock,n):
lock.acquire() # 锁住
print('%s: %s is running' % (n, os.getpid()))
time.sleep(random.random())
print('%s: %s is done' % (n, os.getpid()))
lock.release() # 释放锁头
if __name__ == '__main__':
lock=Lock() # 写在主进程是为了让子进程拿到同一把锁.
for i in range(3):
p=Process(target=work,args=(lock,i))
p.start()
# p.join()
"""
进程锁 是把锁住的代码变成了串行
join 是把所有的子进程变成了串行
为了保证数据的安全,串行牺牲掉效率.
"""

0: 46160 is running

0: 46160 is done

1: 56236 is running

1: 56236 is done

2: 53824 is running

2: 53824 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(10): # 模拟并发100个客户端抢票
p=Process(target=task)
p.start()

剩余票数3

剩余票数3

剩余票数3

剩余票数3

剩余票数3

剩余票数3

剩余票数3

剩余票数3

剩余票数3

剩余票数3

购票成功

购票成功

购票成功

购票成功

购票成功

购票成功

购票成功

购票成功

购票成功

购票成功

四、使用锁来保证数据安全

# 文件db的内容为:{"count":2}
# 注意一定要用双引号,不然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(random.random()) # 模拟读数据的网络延迟
if dic['count'] >0:
dic['count']-=1
time.sleep(random.random()) # 模拟写数据的网络延迟
json.dump(dic,open('db','w'))
print('购票成功')
else:
print('购票失败') def task(lock):
search()
lock.acquire()
get()
lock.release() if __name__ == '__main__':
lock = Lock()
for i in range(100): # 模拟并发100个客户端抢票
p=Process(target=task,args=(lock,))
p.start()

剩余票数2

剩余票数2

剩余票数2

剩余票数2

剩余票数2

购票成功

购票成功

购票失败

购票失败

购票失败

加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。

虽然可以用文件共享数据实现进程间通信,但问题是:

  1. 效率低(共享数据基于文件,而文件是硬盘上的数据)
  2. 需要自己加锁处理

因此我们最好找寻一种解决方案能够兼顾:

  1. 效率高(多个进程共享一块内存的数据)
  2. 帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。

队列和管道都是将数据存放于内存中,队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。

最新文章

  1. iOS AVCaptureSession 小视频开发总结,支持设备旋转
  2. Windows 部署 Redis 群集
  3. State Threads——异步回调的线性实现
  4. 二级域名session 共享方案
  5. MYSQL数据库重点:流程控制语句
  6. ESB服务号列表
  7. java集合类(二)List学习
  8. [Python]linux自己定义Python脚本命令
  9. 使用__doPostBack函数来达到使用客户端的控件来调用服务器端的函数的--小结
  10. 用IBM WebSphere DataStage进行数据整合: 第 1 部分
  11. 【一天一道LeetCode】#15 3Sum
  12. iOS逆向开发(0):修改二进制代码与重签名 | hopper | codesigh
  13. 【python-opencv】30-角点检测
  14. 谈谈HashMap线程不安全的体现
  15. [日常] Go语言圣经-函数多返回值习题
  16. BZOJ2721 Violet5樱花(数论)
  17. 使用DbTableColumnWeb项目简要
  18. CentOS7下,防火墙设置
  19. 虚拟机实现finally语句块
  20. day05 字典 dict

热门文章

  1. javascript中的私有作用域
  2. c++程序—字符型
  3. P 1026 程序运行时间
  4. SpringBoot的ApplicationRunner和CommandLineRunner
  5. windows自带的颜色编辑器居中
  6. LabVIEW面向对象的ActorFramework(3)
  7. javascript面向对象编程的3种常见封装形式解析
  8. 完美解决Webpack多页面热加载缓慢问题【转载】
  9. python try-except处理异常的常用方法分析
  10. POJ 1459:Power Network 能源网络