一:僵尸进程与孤儿进程(面试会问到)

主进程需要等待子进程结束后,主进程才结束,主进程时刻监测子进程的运行状态,当子进程结束之后,过一段时间将子进程回收

1.为什么主进程不在子进程结束后立马对其回收呢?

  1. 主进程与子进程是异步关系,主进程无法捕获子进程什么时候结束
  2. 如果子进程结束之后马上释放资源,主进程就无法监测子进程的状态(子进程立马死掉,不热乎了就没办法了)

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.基于文件通信(抢票系统)

主要是一个操作系统

  1. 先可以查票,查询余票数 并发
  2. 进行购买,向服务端发送请求,服务端接收请求,在后端将票数减一,返回到前端
  3. 补充一个知识点,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.基于队列通信

队列 : 把队列理解成一个容器,这个容器可以加载一些数据

  1. 共享的空间
  2. 内存空间
  3. 自动帮我们处理好锁定问题

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 的值超过内存限制,将变得毫无意义

3.基于管道通信

生产者消费模型

from multiprocessing import Process,Queue

import time

import os

import random

def producer(q,name):

for i in range(1,6):

time.sleep(random.randint(1,2))

res = f"{i}号包子"

q.put(res)

print(f"生产者{name}生产了{res}")def consumer(q,name):

while 1:

try:

food = q.get(timeout=3)

time.sleep(random.randint(1, 3))

print(f"消费者{name}吃了{food}")

except Exception:

returnif name == 'main':

q = Queue()

p1 = Process(target = producer,args=(q,'坤坤'))

p2 = Process(target = consumer,args=(q,'海狗'))

p1.start()

p2.start()

'''


生产者坤坤生产了1号包子
消费者海狗吃了1号包子
生产者坤坤生产了2号包子
生产者坤坤生产了3号包子
消费者海狗吃了2号包子
生产者坤坤生产了4号包子
消费者海狗吃了3号包子
生产者坤坤生产了5号包子
消费者海狗吃了4号包子
消费者海狗吃了5号包子
'''