一 守护进程
子进程守护着主进程,只要主进程结束,子进程跟着就结束
from multiprocessing import Process
import time
def task(name):
print(f'{name}is running')
time.sleep(2)
print(f'{name}is gone')
if __name__ == '__main__':
p = Process(target=task,args=('小黑',))
p.daemon = True # 在进程start之前,将p子进程设置成守护进程,只要主进程结束,守护进程马上结束.
p.start()
time.sleep(1)
print('主进程')
二 僵尸进程与孤儿进程
基于Unix环境(linux, macOS)
-
主进程需要等子进程结束之后才结束
主进程时刻监测子进程的运行状态,当子进程结束之后,一段时间之内将子进程进行回收.
-
为什么主进程不会在子进程结束后马上对其回收?
主进程与子进程是异步关系,主进程无法马上捕获子进程什么时候结束.
如果子进程结束之后马上在内存中释放资源,主进程就没有办法监测子进程的状态了
-
unix针对上面问题提供了一个机制:
所有的子进程结束之后,立马会释放掉文件的操作链接,内存的大部分数据,但是会保留一些内容:进程号,结束时间,运行状态,等待主进程监测,回收.
-
僵尸进程:所有的子进程结束之后,在被主进程回收之前,都会进入僵尸进程状态.
危害: 如果主进程不对僵尸进程回收(wait/waitpid),产生大量的僵尸进程,这样就会占用内存,占用进程pid号.
解决: 直接杀死主进程,将所有的僵尸进程变成孤儿进程,由init进行回收.
-
孤儿进程: 主进程由于某种原因结束了,但是子进程还在运行,这些子进程就变成了孤儿进程.主进程结束了,所有的孤儿进程就会被init进行回收,init就变成了主进程,将其进行回收.
三 互斥锁
from multiprocessing import Process
import time
import random
import os
import json
from multiprocessing import Lock
def task(lock):
lock.acquire()
print(f'{os.getpid()}开始打印')
time.sleep(random.randint(1,3))
print(f'{os.getpid()}打印结束')
lock.release()
if __name__ == '__main__':
lock = Lock()
for i in range(3):
p = Process(target=task,args=(lock,))
p.start()
当很多进程共抢一个资源(数据)时,要保证其顺序进行(数据的安全),这时要给它加一把锁,使其串行,这把锁就叫互斥锁.
互斥锁: 可以公平性的保证顺序以及数据的安全.
lock 和 join 的区别
共同点: 都可以把并发变成串行,保证了程序有顺进行
不同点: join 人为设定顺序,lock 让其争抢顺序,保证了公平性.
四 进程之间的通信
1. 基于文件通信:
进程在内存级别是隔离的,但是文件在磁盘上,可以对其访问
from multiprocessing import Process
import time
import random
import os
import json
from multiprocessing import Lock
def search():
time.sleep(random.randint(1,3))
with open('t1.json',encoding='utf-8')as f:
dic = json.load(f)
print(f'{os.getpid()}查看了票数,还剩{dic["count"]}张票')
def buy():
with open('t1.json', encoding='utf-8')as f:
dic = json.load(f)
if dic["count"] > 0:
dic['count'] -= 1
time.sleep(random.randint(1, 3))
with open('t1.json','w',encoding='utf-8')as f1:
json.dump(dic,f1)
print(f'{os.getpid()}购票成功')
else:
print('没票了')
def task(lock):
search()
lock.acquire()
buy()
lock.release()
if __name__ == '__main__':
lock = Lock()
for i in range(6):
p = Process(target=task,args=(lock,))
p.start()
基于文件的进程之间的通信: 效率低, 自己加锁而且容易出现死锁.
2. 基于队列通信
队列的特性: 先进先出(FIFO),永远保持这个数据.
from multiprocessing import Queue
q = Queue(3) # maxsize 最大容量
q.put(1)
q.put('asdr')
q.put([1,2,3])
q.put(666,block=False) # 不写block默认为True,超过最大容量会阻塞,block = False直接报错
q.put(666,timeout=3) # 延迟3s之后还阻塞直接报错
print(q.get())
print(q.get())
print(q.get())
print(q.get(block=False))
print(q.get(timeout=3))
五 生产者消费者模型
生产者消费者模型三要素:
生产者: 产生数据
消费者: 接收数据做进一步处理
容器: 队列
队列容器的作用: 起到缓冲的作用,平衡生产力与消费力,解耦
from multiprocessing import Process,Queue
import time,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:
return
if __name__ == '__main__':
q = Queue()
p1 = Process(target=producer,args=(q,'孙宇'))
p2 = Process(target=consumer,args=(q,'海狗'))
p1.start()
p2.start()