python之并发编程-进程之间的通信

进程之间的通信

  1. 互斥锁

    进程之间数据不共享,但共享同一套文件系统,或同一个打印终端是没有问题的。但共享导致了竞争,若不加以控制就会造成错乱。如下:

    from multiprocessing import Process
    import time
    import random
    def task1():
        print('task1开始打印')
        time.sleep(random.randint(1,3))
        print('task1打印完成')
    def task2():
        print('task2开始打印')
        time.sleep(random.randint(1,3))
        print('task2打印完成')
    def task3():
        print('task3开始打印')
        time.sleep(random.randint(1,3))
        print('task3打印完成')
    if __name__ == '__main__':
        p1 = Process(target=task1,)
        p2 = Process(target=task2,)
        p3 = Process(target=task3,)
        p1.start()
        p2.start()
        p3.start()
    # task1开始打印
    # task2开始打印
    # task3开始打印
    # task1打印完成
    # task3打印完成
    # task2打印完成

    使用加锁可以使并发变成串行,牺牲效率,避免竞争

    from multiprocessing import Process
    from multiprocessing import Lock
    import time
    import random
    def task1(lock):
        lock.acquire()
        print('task1开始打印')
        time.sleep(random.randint(1,3))
        print('task1打印完成')
        lock.release()
    def task2(lock):
        lock.acquire()
        print('task2开始打印')
        time.sleep(random.randint(1,3))
        print('task2打印完成')
        lock.release()
    def task3(lock):
        lock.acquire()
        print('task3开始打印')
        time.sleep(random.randint(1,3))
        print('task3打印完成')
        lock.release()
    if __name__ == '__main__':
        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()
    # task1开始打印
    # task1打印完成
    # task2开始打印
    # task2打印完成
    # task3开始打印
    # task3打印完成

    利用多进程抢票:

    from multiprocessing import Process
    from multiprocessing import Lock
    import time
    import random
    import json
    def search():
        time.sleep(random.random())  # 模拟读取数据网络延迟
        with open('db.json',encoding='utf-8') as f:
         dic = json.load(f)
     print(f'剩余票数{dic["count"]}')
    def get():
        with open('db.json',encoding='utf-8') as f:
            dic = json.load(f)
        time.sleep(random.random())  # 模拟读取数据网络延迟
        if dic['count'] > 0:
            dic['count'] -= 1
            time.sleep(random.random())  # 模拟写数据网络延迟
            with open('db.json',encoding='utf-8',mode='w') as f:
                json.dump(dic,f)
            print(f'{os.getpid()}用户购买成功')
        else:
            print('票没了')
    def task(lock):
        search()
        lock.acquire()
        get()
        lock.release()
    if __name__ == '__main__':
        lock = Lock()
        for i in range(5):
         p = Process(target=task,args=(lock,))
         p.start()
    # 剩余票数1
    # 剩余票数1
    # 14004用户购买成功
    # 剩余票数0
    # 剩余票数0
    # 票没了
    # 剩余票数0
    # 票没了
    # 票没了
    # 票没了

    虽然可以用文件共享数据实现进程间通信,但问题是:

    1. 效率低(共享数据居于文件,而文件是硬盘上的数据)
    2. 需要自己加锁处理,容易形成死锁、递归锁(注意一个进程只能使用一个锁)
  2. 队列

    multiprocessing模块支持两种形式:队列和管道,这两种方式都是传递数据的。

    队列就是存在于内存中的一个容器,最大的特点就是FIFO,完全支持先进先出原则

    1. 创建队列的类(底层就是以管道和索的方式实现的)

      Queue([maxsize]):创建共享的进程队列,Queue是多进程安全队列,可以使用Queue实现多进程之间的数据传递

    2. 参数介绍

      maxsize是队列中允许最大项数,省略则无大小限制

    3. 方法介绍

      • 主要方法

        # q.put方法用以插入数据到队列中,put方法还有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,该方法会阻塞timeout指定的时间,直到该队列有剩余的空间。如果超时,会抛出Queue.Full异常。如果blocked为False,但该Queue已满,会立即抛出Queue.Full异常。
        # q.get方法可以从队列读取并且删除一个元素。同样,get方法有两个可选参数:blocked和timeout。如果blocked为True(默认值),并且timeout为正值,那么在等待时间内没有取到任何元素,会抛出Queue.Empty异常。如果blocked为False,有两种情况存在,如果Queue有一个值可用,则立即返回该值,否则,如果队列为空,则立即抛出Queue.Empty异常.  
        # q.get_nowait():同q.get(False)
        # q.put_nowait():同q.put(False) 
        # q.empty():调用此方法时q为空则返回True,该结果不可靠,比如在返回True的过程中,如果队列中又加入了项目。
        # q.full():调用此方法时q已满则返回True,该结果不可靠,比如在返回True的过程中,如果队列中的项目被取走。
        # q.qsize():返回队列中目前项目的正确数量,结果也不可靠,理由同q.empty()和q.full()一样
      • 其他方法

        # q.cancel_join_thread():不会在进程退出时自动连接后台线程。可以防止join_thread()方法阻塞
        # q.close():关闭队列,防止队列中加入更多数据。调用此方法,后台线程将继续写入那些已经入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将调用此方法。关闭队列不会在队列使用者中产生任何类型的数据结束信号或异常。例如,如果某个使用者正在被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
        # q.join_thread():连接队列的后台线程。此方法用于在调用q.close()方法之后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread方法可以禁止这种行为
  3. 进程之间的通信实例

    利用队列进程之间通信:简单,方便,不用自己动手加锁。队列自带阻塞,可持续化取数据。

    # 用队列来存储网络请求,模拟网购抢购
    import os
    from multiprocessing import Queue
    from multiprocessing import Process
    def task(q):
        try:
            q.put(f'{os.gerpid()}',block=False)  # 模拟存放请求
        except Exception:
            return
    if __name__ == '__name__':
        q = Queue(10)
        for i in range(100):
            p = Process(target=task,args=(q,))
            p.start()
        for i in range(1,11):
            print(f'排名第{i}的用户:{q.get()}')    # 获取前十个请求
  4. 生产者与消费者模型

    生产者:生产数据进程

    消费者:对生产者生产出来的数据作进一步处理的进程

    生产者与消费者之间需要有一个容器作为缓冲区,平衡生产力与消费力

    生产者消费者模型多用于并发

    from multiprocessing import Process
    from multiprocessing import Queue
    import time
    import random
    def producer(name,q):
        for i in range(1,6):
            time.sleep(random.randint(1,3))
            ret = f'{i}号包子'
            q.put(ret)
            print(f'\033[0;32m 生产者{name}:生产了{ret}\033[0m')
    def consumer(name,q):
        while 1:
            try:
                time.sleep(random.randint(1,3))
                ret = q.get(timeout=5)
                print(f'消费者{name}:吃了{ret}')
            except Exception:
                return
    if __name__ == '__main__':
        q = Queue()
        p1 = Process(target=producer,args=('铁憨憨',q))
        p2 = Process(target=consumer,args=('皮皮寒',q))
        p1.start()
        p2.start()
    # 生产者铁憨憨:生产了1号包子
    #消费者皮皮寒:吃了1号包子
    # 生产者铁憨憨:生产了2号包子
    #消费者皮皮寒:吃了2号包子
    # 生产者铁憨憨:生产了3号包子
    # 生产者铁憨憨:生产了4号包子
    #消费者皮皮寒:吃了3号包子
    # 生产者铁憨憨:生产了5号包子
    #消费者皮皮寒:吃了4号包子
    #消费者皮皮寒:吃了5号包子
  5. 总结

    进程之间的通信:

    1. 基于文件+ 锁的形式: 效率低,麻烦.
    2. 基于队列: 推荐使用形式.
    3. 基于管道: 管道自己加锁, 底层可以会出现数据丢失损坏.

    多个进程抢占一个资源: 串行,有序以及数据安全.

    多个进程实现并发的效果: 生产者消费者模型.

猜你喜欢

转载自www.cnblogs.com/yaoqi17/p/11240298.html