zoukankan      html  css  js  c++  java
  • 守护进程,互斥锁, IPC ,Queue队列,生产消费着模型

    1.守护进程

    什么是守护进程?

      进程是一个正在运行的程序

      守护进程也是一个普通进程,意思是一个进程可以守护另一个进程,比如如果b是a的守护进程,a是被守护的进程,如果a进程结束,b进程也会随之结束。

    使用场景:

      父进程交给了子进程一个任务,子进程在执行过程中并没有执行完毕,但是父进程结束了,那么子进程就没又继续执行的意义了

    使用方法:在start 前面加 deamon = Ture

    案例:

    from multiprocessing import Process
    import time
    def task():
        print('子进程开启了')
        time.sleep(5)
        print('你跟着去吧')
    
    if __name__ == '__main__':
        print('父进程开始了')
        p = Process(target=task)
    
        # 使用daemon方法来守护父进程,如果父进程提前结束了
        # 那么子进程也会一起结束
        p.daemon = True
        p.start()
        time.sleep(2)
        print('父进程结束了')

    2.互斥锁

    什么是互斥锁?

      互斥锁,意思是互相排斥的锁,在程序中的意思是。如果这个资源已经被锁定了,那么其他的进程则无法使用该资源

    PS: 锁,并不是真的把资源锁起来了,只是在代码层面限制你的代码不能执行

    为什么需要互斥锁?

      并发带来资源的竞争问题:

    当多个进程同时要操作同一个资源时,将会导致数据错乱的问题,那么解决这样问题的方案有两种

      1. 是用 join ,等子进程结束后再执行其他进程,这样做的效果确实能实现资源竞争的问题,但是却降低了效率,如果这样操作就没有必要开启子进程了,而且本来多个进程之间是公平竞争的,而join方法,就必须按照join的执行顺序来进行,所以这是不合理的

      2. 给公共资源加锁  

      每个进程间是公平的,都是同时竞争,但是如果有一个进程抢到了资源,就会把资源锁住,那么其他的程序就只能等哪个进程释放锁之后才能使用。

    锁与join的区别

      1. join是固定了执行顺序,不仅会造成父进程等待子进程结束后才能继续执行,而且浪费资源

      而锁是公平竞争,谁先抢到谁先执行,父进程依然可以继续执行,不受干扰

      2.最主要i的区别

      join是把进程的任务全部串行

      锁可以锁任意代码,可以自主的调整锁定的粒度

    使用方法:

      在你需要执行的代码前加 acpuire 方法,代码的后面加 release

    案例:

    '''
    并发将带来资源的竞争问题
    当多个进程同时要操作同一个资源时,将会导致数据错乱的问题
    
    '''
    
    
    def task1(lock):
        # acquire 将进程上锁(并不是上锁),是让其他进程必须等抢到系统资源
        # 的进程执行完毕后才能执行
        lock.acquire()
        print('哒哒哒哒哒哒冒蓝光的加特林')
        print('biubiubiubiu冒红光的M416')
        print('砰砰砰砰砰砰冒黄光的手榴弹')
        # 当进程执行完后要释放锁,让其他进程使用
        lock.release()
    
    
    def task2(lock):
        lock.acquire()
        print('冒蓝光的加特林')
        print('冒红光的M416')
        print('黄光的手榴弹')
        lock.release()
    
    
    def task3(lock):
        lock.acquire()
        print('加特林')
        print('M416')
        print('手榴弹')
        lock.release()
    
    
    if __name__ == '__main__':
        # 需要先实例化得到一把锁
        # 需要注意的是,不要对同一把执行两次acquire,会锁死
        # 导致程序无法运行,一此acquire必须对应一次release
        lock = Lock()
        p1 = Process(target=task1, args=(lock,))
        p2 = Process(target=task2, args=(lock,))
        p3 = Process(target=task3, args=(lock,))
    
        p1.start()
        p2.start()
        p3.start()

    3.IPC

    IPC = 进程间通讯 (通讯指的是互相交换数据)

    进程之间内存是相互隔离的,当一个进程想要把数据给另外一个进程的时候,就需要考虑IPC

    进程之间传输数据的方式:

      1.管道:只能单向通讯,数据都是二进制(subprocess)

      2.文件:在银盘上创建共享文件

        缺点: 速度慢

           优点: 数据量没有限制

      3.socket

        编程难度复杂

      4. 共享内存 

        优点: 速度块

        缺点: 数据量不能太大

    共享内存的方式

    Manager类

      Manager提供很多数据结构 list dict 等等

      Manager所创建出来的数据结构,具备进程间共享的特点

    案例:

    from multiprocessing import Process,Manager,Lock
    def task(data, lock):
        lock.acquire()
        num = data['num']
    
        data['num'] = num - 1
        lock.release()
    
    
    if __name__ == '__main__':
        # Manager 开启一个共享字典,这个字典子进程与父进程公同拥有
        m = Manager()
        data = m.dict({'num': 10})
        lock = Lock()
    
        for i in range(10):
            p = Process(target=task, args=(data, lock))
            p.start()
            p.join()
        print(data)

    PS: 需要强调的是Manager创建的一些数据结构是不带锁的,可能会出现问题

    4.Queue队列  *****(重点)

    Queue队列帮我们处理了锁的问题,重点

      队列是一种特殊的数据结构,先存储的先取出,先进先出

      相反的是堆区,先存储的后取出,先进后出

    PS:函数嵌套调用时,执行顺序是先进后出的,所以也称之为函数栈

      调用函数时,函数入栈,函数结束就出栈

    案例:

    from multiprocessing import Queue
    
    # 创建队列,不指定maxsize 则没有数量限制
    q = Queue(3)
    
    # 存储元素
    q.put('abc')
    q.put('123')
    q.put('哈哈哈')
    
    print(q.get())
    print(q.get())
    print(q.get())
    
    # 如果容量已经满了,在调用put时将会进入阻塞状态
    # 直到有人从队列中拿走数据有空位置才会继续执行
    
    
    # 如果队列已经空了,在调用get时将进入阻塞状态,
    # 直到有人存储了新的数据到队列中,才会继续
    
    
    # block 表示是否需要进行阻塞,默认是阻塞的,当设置为False,并且队列为空时,则抛出异常
    # timeout 表示阻塞的超时时间,超过时间还是没有值或还是没位置则抛出异常,仅在block为True有效
    
    # 此处如果没有值,则会阻塞两秒,如果还没有值,则报异常
    # q.get(block=True,timeout=2)
    
    # 此处存值时,如果容器满了,则不会阻塞,直接报异常
    # q.put('123', block=False)

    5.生产消费着模型

    生产者:产生数据的一方

    消费者:处理数据的一方

    生产者与消费者中存在的问题:

    生产者和消费者处理速度不平衡,一方快一方慢,导致一方需要等待另一方

    双方是耦合在一起的,消费者必须等待生产者生成完毕后才能开始处理,

    但是如果消费者消费速度太慢,生产者必须等待其处理完毕才能开始生成下一个数据

    解决途径:

      将双方分开,生产者专门生产,消费者专门消费,两者不需要等待某一方结束后才能执行

    但是这样一来就需要一个共同的容器,生产者完成后放入容器,消费者从容器中取出数据,这样就解决了双方能力不平衡的问题,完成时间快的一方继续完成,无需等待另一方

    案例:

    from multiprocessing import Process, Queue, Lock
    import time, random, os
    
    # 消费者
    def consumer(q):
        # 假如消费者点了10盘菜
        for i in range(10):
            # 因为吃饭需要时间,所以使用tiem模拟了时间
            time.sleep(random.randint(0, 2))
            # 通过从Queue容器获取生产者的数据
            rose = q.get()
            print('%s已经吃完了%s' % (os.getpid(), rose))
    
    
    # 生产者
    def producer(q):
        # 生产者需要烧制10盘菜
        for i in range(10):
            # 因为制作菜需要时间,所以也使用了tiem 模拟了时间
            time.sleep(random.randint(0, 2))
            print('第%s盘菜烧好了' % i)
            rose = ('第%s盘菜' % i)
            # 使用put方法往Queue中添加数据
            q.put(rose)
    
    if __name__ == '__main__':
        # 创造一个Queue来存储数据
        q = Queue()
        c = Process(target=consumer,args=(q,))
        p = Process(target=producer,args=(q,))
        c.start()
        p.start()
  • 相关阅读:
    Java基础中的一些注意点(续)
    Java基础中的一些注意点
    Java基础知识学习
    JavaScript DOM2
    JavaScript window
    函数
    JavaScript数组
    JavaScript循环练习2
    JavaScript循环练习
    JavaScript循环
  • 原文地址:https://www.cnblogs.com/liguodeboke/p/10969684.html
Copyright © 2011-2022 走看看