并发编程 ~~~ 多进程~~~守护进程, 僵尸进程与孤儿进程, 互斥锁, 进程之间的通信, 生产者消费者模型

一 守护进程

子进程守护着主进程,只要主进程结束,子进程跟着就结束

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)

  1. 主进程需要等子进程结束之后才结束

    主进程时刻监测子进程的运行状态,当子进程结束之后,一段时间之内将子进程进行回收.

  2. 为什么主进程不会在子进程结束后马上对其回收?

    主进程与子进程是异步关系,主进程无法马上捕获子进程什么时候结束.

    如果子进程结束之后马上在内存中释放资源,主进程就没有办法监测子进程的状态了

  3. unix针对上面问题提供了一个机制:

    所有的子进程结束之后,立马会释放掉文件的操作链接,内存的大部分数据,但是会保留一些内容:进程号,结束时间,运行状态,等待主进程监测,回收.

  4. 僵尸进程:所有的子进程结束之后,在被主进程回收之前,都会进入僵尸进程状态.

    危害: 如果主进程不对僵尸进程回收(wait/waitpid),产生大量的僵尸进程,这样就会占用内存,占用进程pid号.

    解决: 直接杀死主进程,将所有的僵尸进程变成孤儿进程,由init进行回收.

  5. 孤儿进程: 主进程由于某种原因结束了,但是子进程还在运行,这些子进程就变成了孤儿进程.主进程结束了,所有的孤儿进程就会被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()

猜你喜欢

转载自www.cnblogs.com/lav3nder/p/11802258.html