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 信号量除了可以进程间同步,还 ...
随机推荐
- bash:haoop:command not found
今天重新搭建了一个3节点的Hadoop集群,想着在上面测试一个MapReduce实例,然后就出现了以下错误: [hadoop@master hadoop-]$ hadoop -bash: hadoop ...
- Aura Component Skills & Tools
本篇参考: https://trailhead.salesforce.com/content/learn/modules/lex_dev_lc_vf_fundamentals 不知不觉已经做了三年多的 ...
- vuex文档(附加个人理解)
Vuex是什么? Vuex 是一个专为 Vue.js应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.Vuex 也集成到Vue 的 ...
- jQuery 获取和设置表单元素
jQuery提供了val()方法,使用它我们可以快速地获取和设置表单的文本框.单选按钮.以及单选按钮的值. 使用val()不带参数,表示获取元素的值 使用val()给定参数,则表示把值赋给元素 如下: ...
- NOIP2013Day1T3 表示只能过一个点
•A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的 ...
- wow.js+animate.css——有趣的页面滚动动画
今天偶然间发现了一个使用特别简单的页面动画效果,还挺不错的,玩了一个上午,现在介绍一下这个滚动动画: 一.使用方法: 1.下载animate.css 2.下载wow.js 3.引用文件,像这样: &l ...
- 零基础逆向工程30_Win32_04_资源文件_消息断点
1 资源文件,创建对话框 详细步骤: 1.创建一个空的Win32应用程序 2.在VC6中新增资源 File -> New -> Resource Script 创建成功后会新增2个文件:x ...
- redis空间键详解
前言 redis的空间键通知是在2.8.0版本以后加入的,客户端通过发布订阅的方式,订阅某个频道,接收通过某种方式影响redis中数据的事件. 目录: 1.空间键事件分类 2.如何启用redis的空间 ...
- 设置mapcontrol的鼠标样式
http://blog.itpub.net/14999074/viewspace-586515/ mapcontrol的鼠标样式 this.axMapControl1.MousePointer=esr ...
- JavaScript获取URL参数公共方法
写一个JavaScript获取URL参数的通用方法,可以把它放到常用方法的JS文件中调用,直接上代码例子,新手可以学习一下! <!DOCTYPE html> <html lang=& ...