一:僵尸进程与孤儿进程(面试会问到)
主进程需要等待子进程结束后,主进程才结束,主进程时刻监测子进程的运行状态,当子进程结束之后,过一段时间将子进程回收
1.为什么主进程不在子进程结束后立马对其回收呢?
- 主进程与子进程是异步关系,主进程无法捕获子进程什么时候结束
- 如果子进程结束之后马上释放资源,主进程就无法监测子进程的状态(子进程立马死掉,不热乎了就没办法了)
2.unix 针对上面的内容提供了一个机制
所有子进程结束后,立马释放掉文件的操作链接,内存的大部分数据,但是会保留一个内容 :进程号、结束时间、运行状态
3.僵尸进程
所有的子进程结束之后,在被主进程回收之前,都会进入僵尸状态
4.僵尸进程有无危害
如果父进程不对子进程进行回收,产生大量的僵尸进程,这样就会占用内存,占用进程 pid 号
5.孤儿进程
父进程由于某种原因结束了,但是你的子进程还在运行中,这样你的子进程就成为了孤儿进程,但是如果你的父进程结束了,你的所有孤儿进程就会被 init 进程回收,对你进行回收
init 就是充当了生活中的孤儿院
6.僵尸进程如何解决(直接杀死父进程)
父进程产生大量子进程,但是不作为不回收,这样就会形成大量的僵尸进程,解决方式就是直接杀死父进程,将所有的僵尸进程变成孤儿进程,由 init 回收
二:互斥锁(保证数据安全,自己加锁容易死锁)
三个同事 同时用一个打印机打印内容. 三个进程模拟三个同事, 输出平台模拟打印机.
版本一
存在的问题 : 现在是所有进程并发的抢占打印机,并发是以效率优先,并发是以效率优先的,但是我们现在是以顺序优先的,多个进程抢占一个资源时必须保证资源,要保证顺序优先,串行,一个一个来
from multiprocessing import Process import time import os import random def task1(): print(f"{os.getpid()}开始打印了") time.sleep(random.randint(1,3)) print(f"{os.getpid()}打印结束了") def task2(): print(f"{os.getpid()}开始打印了") time.sleep(random.randint(1, 3)) print(f"{os.getpid()}打印结束了") def task3(): print(f"{os.getpid()}开始打印了") time.sleep(random.randint(1, 3)) print(f"{os.getpid()}打印结束了") if __name__ == '__main__': p1 = Process(target = task1) p2 = Process(target = task2) p3 = Process(target = task3) p1.start() p2.start() p3.start() """ 8067开始打印了 8068开始打印了 8069开始打印了 8068打印结束了 8067打印结束了 8069打印结束了 一个字概括 乱 """
版本二 :
利用了 join 解决了串行问题,保证了顺序优先,但是这个谁先谁后是固定的,这样是不对的,你在争抢同一个资源时,顺序应该是不固定的……所以引出了下面的互斥锁
from multiprocessing import Process import time import os import random def task1(): print(f"{os.getpid()}开始打印了") time.sleep(random.randint(1,3)) print(f"{os.getpid()}打印结束了") def task2(): print(f"{os.getpid()}开始打印了") time.sleep(random.randint(1, 3)) print(f"{os.getpid()}打印结束了") def task3(): print(f"{os.getpid()}开始打印了") time.sleep(random.randint(1, 3)) print(f"{os.getpid()}打印结束了") if __name__ == '__main__': p1 = Process(target = task1) p2 = Process(target = task2) p3 = Process(target = task3) p1.start() p1.join() p2.start() p2.join() p3.start() p3.join() """ 9026开始打印了 9026打印结束了 9031开始打印了 9031打印结束了 9038开始打印了 9038打印结束了
lock 与 join 的区别:
共同点 :都可以将并发变成串行,保证了执行顺序
不同点: join 是人为设定顺序,lock 让其争抢顺序,保证了公平性
from multiprocessing import Process from multiprocessing import Lock import time import os import random def task1(p,lock): lock.acquire() print(f'{p}开始打印了') time.sleep(random.randint(1,3)) print(f'{p}打印结束了') lock.release() def task2(p,lock): lock.acquire() print(f'{p}开始打印了') time.sleep(random.randint(1,3)) print(f'{p}打印结束了') lock.release() def task3(p,lock): lock.acquire() print(f'{p}开始打印了') time.sleep(random.randint(1,3)) print(f'{p}打印结束了') lock.release() if __name__ == '__main__': mutex = Lock() p1 = Process(target = task1,args = ('p1',mutex)) p2 = Process(target = task1,args = ('p2',mutex)) p3 = Process(target = task1,args = ('p3',mutex)) p1.start() p2.start() p3.start() ''' p1开始打印了 p1打印结束了 p2开始打印了 p2打印结束了 p3开始打印了 p3打印结束了 '''
三:进程之间的通信
1.基于文件通信(抢票系统)
主要是一个操作系统
- 先可以查票,查询余票数 并发
- 进行购买,向服务端发送请求,服务端接收请求,在后端将票数减一,返回到前端
- 补充一个知识点,json 中,字典所有的都是双引号
但是当多个进程共抢一个数据时,如果要保证数据的安全,必须要串行.
要想让购买环节进行串行,加锁
下面进入加锁环节
from multiprocessing import Process from multiprocessing import Lock import time import os import random import json def search(): time.sleep(random.randint(1,3)) with open('ticket.json',mode = "r",encoding = "utf-8")as f1: dic = json.load(f1) print(f"{os.getpid()}查看了票数,剩余{dic['count']}") def paid(): with open('ticket.json', encoding="utf-8")as f1: dic = json.load(f1) if dic['count'] > 0: dic['count'] -= 1 time.sleep(random.randint(1, 3)) with open('ticket.json', mode="w", encoding="utf-8")as f1: json.dump(dic,f1) print(f"{os.getpid()}购买成功") def task(lock): search() lock.acquire() paid() lock.release() if __name__ == '__main__': mutex = Lock() for i in range(6): p = Process(target = task,args = (mutex,)) p.start() ''' 75543查看了票数,剩余1 75544查看了票数,剩余1 75548查看了票数,剩余1 75545查看了票数,剩余1 75543购买成功 75546查看了票数,剩余0 75547查看了票数,剩余0 ''''
总结:当很多资源共抢一个资源数据时,你要保证顺序(数据的安全),一定要串行
互斥锁:可以公平性的保证顺序以及数据的安全
基于文件进程之间的通信 : 效率低,自己加锁麻烦,而且很容易出现死锁
2.基于队列通信
队列 : 把队列理解成一个容器,这个容器可以加载一些数据
- 共享的空间
- 内存空间
- 自动帮我们处理好锁定问题
Queue的特性 : 先进先出 FIFO
from multiprocessing import Queue q = Queue() def func(): print('in func') q.put(1) q.put("胖") q.put([1,2,3,4]) q.put(func) print(q.get()) print(q.get()) print(q.get()) print(q.get()) ''' 1 胖 [1, 2, 3, 4] <function func at 0x109c1ba60> '''
from multiprocessing import Queue q = Queue(3) q.put(1) q.put("alex") q.put([1,2,3]) q.put(555) # 当队列满了时,在进程 put 就会阻塞 print(q.get()) print(q.get()) print(q.get()) print(q.get()) # 当数据取完时,在进程get数据也会出现阻塞,直到某一个进程put数据. ''' 阻塞之后就直接不运行了,但是不报错 '''
block
from multiprocessing import Queue q = Queue(3) q.put(1) q.put('alex') q.put([1,2,3]) print(q.get()) print(q.get()) print(q.get()) print(q.get(block = False )) ''' 只要遇到 block = False 阻塞就会报错 '''
timeout
from multiprocessing import Queue q = Queue(3) q.put(1) q.put('alex') q.put([1,2,3]) print(q.get()) print(q.get()) print(q.get()) print(q.get(timeout = 3)) ''' 阻塞 3 秒 3秒之后还阻塞就报错 '''
队列存成数据间沟通的消息时,数据量不应该过大
maxsize 的值超过内存限制,将变得毫无意义