20181229(守护进程,互斥锁,IPC,生产者和消费者模型)
一、守护进程
守护进程:一个进程B守护另一个进程A,当被守护的进程A结束,进程B也就结束了。(不一定同生,但会同死)
两个特点:
①守护进程会在主进程代码执行结束后就终止
②守护进程内无法再开启子进程,否则抛出异常。
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
应用场景:如果主进程认为一旦自己结束,子进程也就没有继续运行的必要了,就可以将子进程设置为守护进程。(例如:qq正在调用自身的下载文件功能,但是此时退出了qq,下载进程也就可以直接关闭了)
方法为:process.daemon=True,必须放置在子进程开启前。
from multiprocessing import Process
import time
def task(name):
print('%s is running' % name)
time.sleep(3)
print('%s is running' % name) # 等待三秒的时候,被守护进程已经执行完了,所以守护进程会被回收,此句话就不会打印了。
if __name__ == '__main__':
obj = Process(target=task, args=('守护进程',))
obj.daemon=True # 必须在start前设置,会限制p创建子进程,且父进程结束子进程马上殉葬结束。
obj.start() # 发送信号给操作系统
time.sleep(1)
print('被守护进程')
输出结果:
守护进程 is running
被守护进程
二、互斥锁
互斥锁:将并发变为串行(即一个一个运行)
from multiprocessing import Process,Lock
import time,random
mutex=Lock() # 实例化互斥锁,也可以放到main下面。
# 强调:必须是lock.acquire()一次,然后 lock.release()释放一次,才能继续lock.acquire(),不能连续的lock.acquire(),否则会形成阻塞,程序卡死。
def task1(lock):
lock.acquire() # 得到这把锁,此时其他的同级子进程都只能等待当前进程将锁释放之后才有机会运行。
print('task1:名字是egon')
time.sleep(random.randint(1,3))
print('task1:性别是male')
time.sleep(random.randint(1,3))
print('task1:年龄是18')
lock.release() # 锁用完之后,一定要释放,否则其他子进程无法进行
def task2(lock):
lock.acquire() # 是一个阻塞的函数 会等到别的进程释放锁才能继续执行
print('task2:名字是alex')
time.sleep(random.randint(1,3))
print('task2:性别是male')
time.sleep(random.randint(1,3))
print('task2:年龄是78')
lock.release()
def task3(lock):
lock.acquire()
print('task3:名字是lxx')
time.sleep(random.randint(1,3))
print('task3:性别是female')
time.sleep(random.randint(1,3))
print('task3:年龄是30')
lock.release()
if __name__ == '__main__':
p1=Process(target=task1,args=(mutex,)) # 创建进程并传参,将锁传给各个子进程
p2=Process(target=task2,args=(mutex,))
p3=Process(target=task3,args=(mutex,))
p1.start() # 三个子程序都能启动,但是一旦碰到锁,谁先抢到谁先运行,其他的要等。
p2.start()
p3.start()
互斥锁与join的区别:
二者原理都一样,都是将并发变为串行,从而保证数据增删改查的有序性,不至于让数据发生错乱。
二者的区别在于join是按照人为指定顺序执行,进程执行顺序是固定的,而互斥锁是所有进程公平竞争,谁先抢到锁,谁就能先执行;且互斥锁可以指定某一部分代码串行,其余部分代码并发,而join则是整个子进程代码完全串行。
互斥锁的本质是一个布尔类型的数据,在执行代码前,会先判断这个值。
互斥锁在抢票中的应用:
import json
from multiprocessing import Process,Lock
import time
import random
# 查看剩余票数
def check_ticket(usr):
time.sleep(random.randint(1,3))
with open("ticket.json","r",encoding="utf-8") as f: # 提前做好json文件,做成字典。
dic = json.load(f)
print("%s查看 剩余票数:%s" % (usr,dic["count"]))
def buy_ticket(usr):
with open("ticket.json","r",encoding="utf-8") as f:
dic = json.load(f)
if dic["count"] > 0:
time.sleep(random.randint(1,3))
dic["count"] -= 1
with open("ticket.json", "w", encoding="utf-8") as f2: #
json.dump(dic,f2)
print("%s 购票成功!" % usr)
else:
print("票被抢走了!%s购票失败" % usr)
def task(usr,lock):
check_ticket(usr) # 查票可以并发
lock.acquire() # 排票就要串行了
buy_ticket(usr)
lock.release() # 买好票后要释放锁
if __name__ == '__main__':
lock = Lock()
for i in range(10): # 启动10个进程去买票,模仿10个人。
p = Process(target=task,args=("用户%s" % i,lock))
p.start()
输出结果: #每次结果可能都不一致
用户0查看 剩余票数:1
用户2查看 剩余票数:1
用户3查看 剩余票数:1
用户1查看 剩余票数:1
用户0 购票成功!
票被抢走了!用户2购票失败
票被抢走了!用户3购票失败
票被抢走了!用户1购票失败
用户5查看 剩余票数:0
票被抢走了!用户5购票失败
用户4查看 剩余票数:0
票被抢走了!用户4购票失败
用户7查看 剩余票数:0
票被抢走了!用户7购票失败
用户6查看 剩余票数:0
票被抢走了!用户6购票失败
用户9查看 剩余票数:0
票被抢走了!用户9购票失败
用户8查看 剩余票数:0
票被抢走了!用户8购票失败
Rlock
RLock 表示可重入锁,其特点是可以多次执行acquire而不会阻塞。
如果在多进程中使用Rlock,并且一个进程a 执行了多次acquire,其他进程b要想获得这个锁,需要进程a把锁解开,并且锁了几次就要解几次。
普通锁如果多次执行acquire将会锁死(阻塞)
from multiprocessing import RLock,Process
import time
def task(i,lock):
lock.acquire()
lock.acquire()
print(i)
time.sleep(3)
lock.release()
lock.release() #第一个过来 睡3秒 第二个过来了 睡3秒 第一个打印1 第二个打印2
if __name__ == '__main__':
lock = RLock() # 重入锁,即多次锁
p1 = Process(target=task,args=(1,lock))
p1.start()
p2 = Process(target=task,args=(2,lock))
p2.start()
死锁:指锁无法打开,导致程序无法继续运行。即两把锁交给了两个人,这两个人还都需要对方的那把锁,互相谁也无法释放锁,也得不到对方的锁。
程序中尽可能不要使用多把锁,防止发生死锁现象
from multiprocessing import Process,Lock
import time
def task1(l1,l2,i):
l1.acquire() # 拿走了l1的锁,但是task2拿走了l2的锁,两个进程后续都无法进行。
print("盘子被%s抢走了" % i)
time.sleep(1)
l2.acquire()
print("筷子被%s抢走了" % i)
print("吃饭..")
l1.release()
l2.release()
def task2(l1,l2,i):
l2.acquire()
print("筷子被%s抢走了" % i)
l1.acquire()
print("盘子被%s抢走了" % i)
print("吃饭..")
l1.release()
l2.release()
if __name__ == '__main__':
l1 = Lock()
l2 = Lock()
Process(target=task1,args=(l1,l2,1)).start()
Process(target=task2,args=(l1,l2,2)).start()
三、IPC通信
IPC:进程间通信, 进程间相互独立,所以要解决进程间相互传输数据的问题。 1、使用共享文件,多个进程同时读写一个文件 受限IO,速度慢 2、管道,基于内存速度快,但是是单向的,比较麻烦 3、申请共享内存空间,进程们可以共享这片区域 基于内存速度快,但是数据量不能太大
from multiprocessing import Process,Manager,Lock # 导入manager
import time
mutex=Lock()
def task(dic,lock):
lock.acquire() # 如果不加锁,都会读取字典,然后修改,结果就是变成9
temp=dic['num']
time.sleep(0.1) # 读取共享空间之后,沉睡0.1秒,让所有进程都能运行起来
dic['num']=temp-1
lock.release()
if __name__ == '__main__':
m=Manager() # 实例化manager
dic=m.dict({'num':10}) # 建立一个字典对象,这是子进程的共享空间
l=[]
for i in range(10):
p=Process(target=task,args=(dic,mutex))
l.append(p)
p.start()
for p in l:
p.join() # 子进程都结束后,再去查看字典
print(dic)
输出结果:
{'num': 0}
如果不加锁,CPU和内存足够快,输出结果可能为{'num':9}
四、队列
一种数据容器,特点是先进先出。(栈是后进先出)
优点:是进程的共享空间,可以自动处理锁的问题(如上小节中如果锁处理不好,就会发生数据错乱)
即便在多进程下,也可以保证数据不会错乱,因为put和get默认阻塞。
注意点:
1、队列用来处理进程间的数据,且在内存中,所以数据量不应过大;
2、限制maxsize的值如果超过了内存就变得毫无意义。
from multiprocessing import Queue
q = Queue(1) # 实例化一个队列,最多可以存一个数据
q.put("张三") # 将数据放进队列中
print(q.get()) # 将数据从队列中拿出
q.put("李四") # 当容器装满时,put默认会阻塞 。如果上一步中没有get,此时就会阻塞
q.put("福布斯",False) # False表示不会阻塞 无论容器是满了 都会强行塞 如果满了就抛异常
print(q.get())
print(q.get()) # 当容器中数据时,get默认阻塞
print(q.get(timeout=3)) # timeout 仅用于阻塞时,此处表示会等待3秒,三秒后如果还取不到数据,就会报错queue.Empty
print("over") # 因为阻塞,此句不会打印
def put(self, obj, block=True, timeout=None): # block表示阻塞,timeout表示等待时间。
pass
def get(self, block=True, timeout=None):
pass
五、生产者消费者模型
生产者:数据的生产者
消费者:处理数据者
生产者消费者模型三要素:生产者、消费者、队列
应用场景:程序中出现明显的两类,一类负责生产数据,另一类负责处理数据时。
该模型优点:
1、实现了数据生产者与数据消费者之间的解耦合
2、平衡了生产力和消费力,即生产者只关心生产即可,消费者只关心处理数据即可,二者通过队列沟通。
import random
from multiprocessing import Process,Queue
import time
# 爬数据
def get_data(q):
for num in range(5):
print("正在爬取第%s个数据" % num)
time.sleep(random.randint(1,2))
print("第%s个数据 爬取完成" % num)
# 把数据装到队列中
q.put("第%s个数据" % num)
def parse_data(q): # 生产者只关心生产即可,队列数最大为5,如果队列满了就进入阻塞状态。
for num in range(5):
# 取出数据
data = q.get()
print("正在解析%s" % data)
time.sleep(random.randint(1, 2))
print("%s 解析完成" % data)
if __name__ == '__main__':
# 共享数据容器
q = Queue(5)
#生产者进程
produce = Process(target=get_data,args=(q,))
produce.start()
#消费者进程
customer = Process(target=parse_data,args=(q,))
customer.start()
20181229(守护进程,互斥锁,IPC,生产者和消费者模型)的更多相关文章
- 守护进程,互斥锁,IPC,队列,生产者与消费者模型
小知识点:在子进程中不能使用input输入! 一.守护进程 守护进程表示一个进程b 守护另一个进程a 当被守护的进程结束后,那么守护进程b也跟着结束了 应用场景:之所以开子进程,是为了帮助主进程完成某 ...
- python并发编程之守护进程、互斥锁以及生产者和消费者模型
一.守护进程 主进程创建守护进程 守护进程其实就是'子进程' 一.守护进程内无法在开启子进程,否则会报错二.进程之间代码是相互独立的,主进程代码运行完毕,守护进程也会随机结束 守护进程简单实例: fr ...
- 进程(守护进程--互斥锁--IPC机制--生产者模型--僵尸进程与孤儿进程--模拟抢票--消息队列)
目录 一:进程理论知识 1.理论知识 二:什么是进程? 三:僵尸进程与孤儿进程 1.僵尸进程 四:守护进程 1.什么是守护进程? 2.主进程创建守护进程 3.守护进程 五:互斥锁(模拟多人抢票) 1. ...
- 守护模式,互斥锁,IPC通讯,生产者消费者模型
'''1,什么是生产者消费者模型 生产者:比喻的是程序中负责产生数据的任务 消费者:比喻的是程序中负责处理数据的任务 生产者->共享的介质(队列)<-消费者 2,为何用 实现了生产者与消费 ...
- 4 并发编程-(进程)-守护进程&互斥锁
一.守护进程 主进程创建子进程,然后将该进程设置成守护自己的进程,守护进程就好比崇祯皇帝身边的老太监,崇祯皇帝已死老太监就跟着殉葬了. 关于守护进程需要强调两点: 其一:守护进程会在主进程代码执行结束 ...
- 8.9 day30 并发编程 进程理论 进程方法 守护进程 互斥锁
多道技术 1.空间上的复用 多个程序共用一套计算机硬件 多道技术原理 2.时间上的复用 切换+保存状态 1.当一个程序遇到IO操作 操作系统会剥夺该程序的CPU执行权限( 提高了CPU的利用率 ...
- python并发编程-进程理论-进程方法-守护进程-互斥锁-01
操作系统发展史(主要的几个阶段) 初始系统 1946年第一台计算机诞生,采用手工操作的方式(用穿孔卡片操作) 同一个房间同一时刻只能运行一个程序,效率极低(操作一两个小时,CPU一两秒可能就运算完了) ...
- 子进程回收资源两种方式,僵尸进程与孤儿进程,守护进程,进程间数据隔离,进程互斥锁,队列,IPC机制,线程,守护线程,线程池,回调函数add_done_callback,TCP服务端实现并发
子进程回收资源两种方式 - 1) join让主进程等待子进程结束,并回收子进程资源,主进程再结束并回收资源. - 2) 主进程 “正常结束” ,子进程与主进程一并被回收资源. from multipr ...
- (day29) 进程互斥锁 + 线程
目录 进程互斥锁 队列和堆栈 进程间通信(IPC) 生产者和消费者模型 线程 什么是线程 为什么使用线程 怎么开启线程 线程对象的属性 线程互斥锁 进程互斥锁 进程间数据不共享,但是共享同一套文件系统 ...
- posix 匿名信号量与互斥锁 示例生产者--消费者问题
一.posix 信号量 信号量的概念参见这里.前面也讲过system v 信号量,现在来说说posix 信号量. system v 信号量只能用于进程间同步,而posix 信号量除了可以进程间同步,还 ...
随机推荐
- Java反射详解(Spring配置)
1. 反射原理 a).运行时通过 Class c = Class.forName("com.hua.xx.DynTest")加载类文件 b).通过 DynTest t = c.ne ...
- SpringBoot | 第二十一章:异步开发之异步调用
前言 上一章节,我们知道了如何进行异步请求的处理.除了异步请求,一般上我们用的比较多的应该是异步调用.通常在开发过程中,会遇到一个方法是和实际业务无关的,没有紧密性的.比如记录日志信息等业务.这个时候 ...
- Java实例学习——企业进销存管理系统(4)
Java实例学习——企业进销存管理系统(4) (本实例为书上实例,我所记录的是我的学习过程) 开始时间:2月12日 完成时间:暂未完成 2月18日——系统主窗体设计 只看了学习视频 2月19日—— 回 ...
- swift 2特性记录
swift 团队一直在优化,让大家准备在秋天的时候,迁移到swift2做准备. 一.错误处理 异常处理,不是NSError对象和双指针. 可以使用 throws 来指定方法来抛出一个错误. 调用d ...
- java 基础 03 运算符 分支结构 循环结构
今天内容: (1)运算符 (2)分支结构 (3)循环结构 1运算符 1.1赋值运算符 (1)简单赋值 = 表示赋值运算符,用于将=右边的数据赋值给=左边的变量来覆盖原来的数值. 笔试题: ia == ...
- Windows8 64位运行Silverlight程序不能访问WCF的解决方案
公司的项目是Silverlight+WCF,而我的本本是Win8 64位系统,一直无法正常运行Silverlight程序,一个同事找到了方案,现分享出来 一种情况是,Vs2010运行程序时,报无法加载 ...
- vue实现选中列表功能
<template> <div> <ul v-for="prop in items"> <dt>{{prop.name}}</ ...
- 云为 | 提供海外 IT 人才派遣、猎头、人力资源外包服务
云为是大连信为软件开发有限公司为人力资源外包服务创建的品牌,是中国专业的人力资源外包领域的服务商,在信息技术行业为海外企业雇主招聘合格.专业且技能熟练的精英人士.我们的客户涵盖了日本上市公司和株式 ...
- javascript设计模式之中介者模式
/* * 小游戏演示中介者模式 * Home 按键 1 * Guest 按键 0 * 半分钟内看谁按下的次数多 * * 参与的对象: * 玩家 * 计分板 * 中介者 * * 中介者模式使对象之间松耦 ...
- notepad++ 等用正则表达式自动添加sql引号(宏)
一般sql语句会经常用到给括号里的内容添加引号,sql如下 Select * From Test ', ', ', ', ', '); 一开始参考了http://blog.sina.com.cn/s/ ...