1. 守护进程
会随着主进程的结束而结束。
主进程创建守护进程
其一:守护进程会在主进程代码执行结束后就终止
其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children
注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止
import os import time from multiprocessing import Process class Myprocess(Process): def __init__(self,person): super().__init__() self.person = person def run(self): print(os.getpid(),self.name) print('%s正在和女主播聊天' %self.person) p=Myprocess('哪吒') p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行 p.start() time.sleep(10) # 在sleep时查看进程id对应的进程ps -ef|grep id print('主') 守护进程的启动
from multiprocessing import Process def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") p1=Process(target=foo) p2=Process(target=bar) p1.daemon=True p1.start() p2.start() time.sleep(0.1) print("main-------")#打印该行则主进程代码结束,则守护进程p1应该被终止.#可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止. 主进程代码执行结束守护进程立即结束
2. 锁 —— 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() 多进程抢占输出资源
# 由并发变成了串行,牺牲了运行效率,但避免了竞争 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() 使用锁维护执行顺序
上面这种情况虽然使用加锁的形式实现了顺序的执行,但是程序又重新变成串行了,这样确实会浪费了时间,却保证了数据的安全。
接下来,我们以模拟抢票为例,来看看数据安全的重要性。
#注意:首先在当前文件目录下创建一个名为db的文件 #文件db的内容为:{"count":1},只有这一行数据,并且注意,每次运行完了之后,文件中的1变成了0, # 你需要手动将0改为1,然后在去运行代码。 #注意一定要用双引号,不然json无法识别 #加锁保证数据安全,不出现混乱 from multiprocessing import Process,Lock import json,time def search(): dic=json.load(open("db"))#打开文件,直接load文件内容,拿到剩余票数 print("剩余票数%s" %dic["count"]) def get(): dic=json.load(open("db")) time.sleep(1)#模拟读数据的网络延迟,那么进程的切换,导致所有人拿到的都是{"count":1},也就是每个人都拿到了这一票 if dic["count"]>0: dic["count"]-=1 time.sleep(0.2) #模拟写数据的网络延迟 json.dump(dic,open("db","w")) print("购票成功")#最终结果是每个都买到票了 else: print("没有票了") def task(): search() lock.acquire() #因为抢票的时候,数据会发生变化,所以我们把锁加在这里 get() lock.release() if __name__ == '__main__': lock=Lock() #创建一把锁 for i in range(3): #创建子进程 p=Process(target=task(),args=(lock,)) #把锁当成参数传给子进程 p.start()
#加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,没错,速度是慢了,但牺牲了速度却保证了数据安全。 虽然可以用文件共享数据实现进程间通信,但问题是: 1.效率低(共享数据基于文件,而文件是硬盘上的数据) 2.需要自己加锁处理 #因此我们最好找寻一种解决方案能够兼顾:1、效率高(多个进程共享一块内存的数据)2、帮我们处理好锁问题。这就是mutiprocessing模块为我们提供的基于消息的IPC通信机制:队列和管道。 队列和管道都是将数据存放于内存中 队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来, 我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可获展性。
3. 信号量 —— multiprocess.Semaphore(了解)
互斥锁同时只允许一个线程更改数据,而信号量Semaphore是同时允许一定数量的线程更改数据 。
假设商场里有4个迷你唱吧,所以同时可以进去4个人,如果来了第五个人就要在外面等待,等到有人出来才能再进去玩。
实现:
信号量同步基于内部计数器,每调用一次acquire(),计数器减1;每调用一次release(),计数器加1.当计数器为0时,acquire()调用被阻塞。这是迪科斯彻(Dijkstra)信号量概念P()和V()的Python实现。信号量同步机制适用于访问像服务器这样的有限资源。
信号量与进程池的概念很像,但是要区分开,信号量涉及到加锁的概念
from multiprocessing import Process,Semaphore import time def func(i,s): s.acquire() #用四把锁,锁住了,得等这四个人爽完了才能让其他人进去爽 print("%s号男嘉宾开始按摩了" %i) time.sleep(1) print("%s号男嘉宾按摩结束了" %i) time.sleep(2) s.release() #四个人爽了了要记得把锁卸下来,不然其他人进不去 if __name__ == '__main__': s=Semaphore(4) #相当于创建了四把锁 for i in range(10): #循环创建了10个子进程 p=Process(target=func,args=(i,s)) #把参数和锁都传给目标函数 p.start() #子进程准备就绪
4, 事件 —— multiprocess.Event(了解)
python线程的事件用于主线程控制其他线程的执行,事件主要提供了三个方法 set、wait、clear。 事件处理的机制:全局定义了一个“Flag”,如果“Flag”值为 False,那么当程序执行 event.wait 方法时就会阻塞,如果“Flag”值为True,那么event.wait 方法时便不再阻塞。 clear:将“Flag”设置为False set:将“Flag”设置为True
from multiprocessing import Process,Event import time def func(e): time.sleep(1) print("子进程的一堆运算") e.set() if __name__ == '__main__': e=Event() p=Process(target=func,args=(e,)) p.start() print("主进程开始运行") e.wait() print("等待子进程通知") print("拿到了子进程的通知,继续执行")
from multiprocessing import Process, Event import time, random def car(e, n): while True: if not e.is_set(): # 进程刚开启,is_set()的值是Flase,模拟信号灯为红色 print('