今日内容

一、守护进程

p.daemon = True  父进程结束时【a进程中创建了子进程b,b标记为】,子进程出跟着结结束 【定义守护进程一定要放在子进程p.start前】

使用场景:如QQ接收到一个视频文件,于是开启了一个子进程来下载,QQ退出了,下载就没必要继续了

 并发安全问题:多个进程争抢资源时,导致数据不安全。如同用一个打印机,打印错乱;抢票系统,余票为1,被多人抢购。

二、互斥锁  将共享的资源锁住,待该进程释放后,其它进程才可以操作,保证了数据的安全有序【本质上是限制了其他进程的执行 】

Lock                                                 等同:  global   my_lock
name = Lock() #产生一个锁对象,                                                       if my_lock == False:
     lock. acquire()   #上锁 【子进程中调用】                                                           my_lock = True
     lock. release()   # 释放角锁                                                                                  被锁住的代码
                                                                                                                                                my_lock = False

    与 join 的区别:【join 也可以保证数据的安全】

    join :  将导致子进程中所有代码全都串行;原本多个进程之间是公平竞争,join后就固定顺序了

    LOCK锁: 可以锁定一部分【即公共资源加锁】代码,其他代码还是并发。

        【 被锁的代码称之为粒度,粒度越大意味着锁住的代码越多,并发的就少了,效率就低,反之亦然

  最主要的区别:join 固定了某进程先执行,且所有进程任务全部串行

        锁维系了并发,让部分代码串行,可以随意锁定代码,自行调整粒度  

   两个注意点:1)想要保住数据安全,必须保证所有进程使用同一把锁

a = Lock()                             a=Lock()
 a.acquire()
print( ' 抢到了!‘ )
a. release()

例:模拟抢票:

from multiprocessing import Process, Lock
import time
import random
import json, os

mute = Lock()

def search(i, lock):
    time.sleep(random.randint(0, 2))
    with open('db.json', 'rt', encoding='utf-8') as f:
        count_dic = json.load(f)
        temp = count_dic['count']
        print('%s查得余票%s' %(i,temp))

def get(i, lock):
    time.sleep(random.randint(0, 3))
    lock.acquire()
    with open('db.json', 'rt', encoding='utf-8') as f:
        count_dic = json.load(f)
        temp = count_dic['count']
        if temp > 0:
            count_dic['count'] -= 1
            #写入文件
            with open('db.json', 'wt', encoding='utf-8') as f:
                json.dump(count_dic,f)
                print('%s抢票成功' % i)
        print('%s 说余票为0' % i)
    lock.release()

def task(i,lock):
    search(i, lock)
    get(i, lock)


if __name__ == '__main__':
    for i in range(10):
        obj = Process(target=task,args=(i,mute))
        obj.start()

三、IPC (inter-Process Communication)  进程与进程间的通信 【进程们对共享数据的操作】

方式:

1)管道:只能单向通信,【数据都是二进制】

2)文件:在硬盘上创意共享文件

    缺点:速度慢                   优点:数据量几乎没有限制

3)socket: 编程复杂度较高

4)进程之间共享的,且在内存中:Manager类

问题:数据错乱需自己加锁处理】

from multiprocessing import Process, Manager, Lock
import time
mutex = Lock()

def task(dic, lock):
    lock.acquire()
    temp = dic['num']
    time.sleep(0.1)
    dic['num'] = temp - 1
    lock.release()

if __name__ == '__main__':
 m = Manager()
    dic = m.dict({'num': 10})

    l = []
    for i in range(10):
        p = Process(target=task, args=(dic,mutex))
        l.append(p)
        p.start()
    for i in l:
        i.join()
    print(dic)

5)multiprocessing 之 Queue 【队列】帮助我们处理了锁的问题 --  IPC机制核心内容:【共享的空间;是内存空间;自动处理锁问题】

【队列用来存进程之间沟通的消息,数据量不应该过大,是实现IPC机制的一种方式】

   对立面是堆栈,先存储的最后取出

【堆栈扩展:函数嵌套调用时,执行顺序是先进后出 ---   也称之为函数栈;调用函数时,函数入栈 函数结束,函数出栈】

队列用法:

from multiprocessing import  Queue
     obj = Queue(maxsize =-1)  # 创建队列,默认没有数量限制,实际也不可能创建无数多个元素【受内存限制】
obj_q = Queue(3)
     obj_q.put('' first'')
  obj_q.put({’second': None})
  obj_q.put([ ])

     obj_q.put('' ooo")  #如果容量已经满了,再调用put时将进入阻塞状态,必须列队中有数据被取出,腾出空位置,才会继续执行放入该数据

print(obj.get())  #first
obj.put(())
print(obj.get())  #{’second': None
print(obj.get())  # [ ]
print(obj.get())  # ooo

q.get(block=True,timeout=2);  block 表示是否阻塞 默认是阻塞的可以等,timeout等的时间,默认无限期等

当block设置为False时,若队列为空则抛出异常,timeout值也就无效

q.put(block=True,timeout=2); block 表示是否阻塞 默认是阻塞的,timeout等的时间,默认无限期等

当设置为False 并且队列满了时 抛出异常,timeout值无效

timeout 表示阻塞的超时时间 ,超过时间还是没有值或还是没位置则抛出异常 【仅在block为True有效】 

 四、生产者与消费者模型:一种解决问题的套路

   处理某问题的方式:解除两类进程之间的对数据的相互依赖,即一类【数据生产者】与另一类【数据使用者】进程处理速度不平衡,一方快一方慢,导致一方需等待另一方

--  该模型中的两类:数据创造者(进程)比喻为生产者、

          接收生产者(进程)造出来的数据进一步处理,该类进程比喻为消费者

          例如:爬虫,爬取数据, 分析数据

--  实现生产者消费者模型三要素: 1. 生产者   2. 消费者    3. 队列

--  该模型的益处/有点:

  1.实现了生产者与消费者解藕合

  2. 平衡了生产力与消费力,即生产者与消费者不直接对接,生产者将数据放入队列,消费者从队列中取

--  该模式的应用场景:

  如果程序中有明显的两类任务,一类任务是负责生产数据,另外一类是处理数据就可以考虑用该模型

 案例1:

import time
import random
from  multiprocessing import Process,Queue

def consumer(name, q):
    while True:
        res = q.get()
        time.sleep(random.randint(1,3))
        print('\033[46m消费者 == > %s 吃了 %s \033[0m'%(name, res))

def producer(name, q, food):
    for i in range(5):
        time.sleep(random.randint(1, 2))
        res = '%s%s'%(food, i)
        q.put(res)
        print('\033[39m生产者===> %s生产了%s\033[0m' %(name, res))

if __name__ == '__main__':
    obj_q = Queue()

    #生产者
    p1= Process(target=producer, args=('egon', obj_q,'玉米'))
    p2= Process(target=producer, args=('jerry', obj_q,'包子'))
    p3= Process(target=producer, args=('owen', obj_q,'米饭'))

    #消费者
    c1 = Process(target=consumer, args=('alex', obj_q,))
    c2 = Process(target=consumer, args=('fanny', obj_q,))

    p1.start()
    p2.start()
    p3.start()
    c1.start()
    c2.start()
----------------------------------------------------------------------------------------------------------------------------------------------------

案例2:

import time
import random
from multiprocessing import Process, Queue


def eat(q):
    for i in range(10):
        rose = q.get()
        time.sleep(random.randint(1, 2))
        print('\033[46m%s吃完了\033[0m' % rose)


def make_rose(q):
    for i in range(10):
        time.sleep(random.randint(0, 3))
        print('\033[45m%s盘西红柿生产完成\033[0m' % i)
        rose = '%s盘西红柿' % i
        q.put(rose)


if __name__ == '__main__':
    q = Queue()
    make_p = Process(target=make_rose, args=(q,))
    eat_p = Process(target=eat, args=(q,))

    make_p.start()
    eat_p.start()