zoukankan      html  css  js  c++  java
  • python 并发编程 锁 / 信号量 / 事件 / 队列(进程间通信(IPC)) /生产者消费者模式

    (1)锁:进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的,而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理。

        虽然使用加锁的形式实现了顺序的执行,但是程序又重新变成串行了,这样确实会浪费了时间,却保证了数据的安全。    (Lock)

    import json
    from multiprocessing import Process,Lock  ######  锁  ######
    import time
    import random
    
    def get_ticket(i,ticket_lock):
        print('我们到齐了')
        time.sleep(1)
        ticket_lock.acquire()
        with open('text', 'r') as f:
            last_ticket_info = json.load(f)
        last_ticket = last_ticket_info['count']
        if last_ticket > 0:
            time.sleep(random.random())
            last_ticket = last_ticket - 1
            last_ticket_info['count'] = last_ticket
            with open('text','w') as f:
                json.dump(last_ticket_info,f)
            print('%s号抢到了。'%i)
        else:
            print("%s号傻逼,没抢到"%i)
        ticket_lock.release()
    
    if __name__ == '__main__':
        ticket_lock = Lock()
        for i in range(10):
            p = Process(target=get_ticket,args=(i,ticket_lock,))
            p.start()
    
    
    ##############
    我们到齐了
    我们到齐了
    我们到齐了
    我们到齐了
    我们到齐了
    我们到齐了
    我们到齐了
    我们到齐了
    我们到齐了
    我们到齐了
    4号傻逼,没抢到
    5号傻逼,没抢到
    0号傻逼,没抢到
    2号傻逼,没抢到
    1号傻逼,没抢到
    3号傻逼,没抢到
    8号傻逼,没抢到
    7号傻逼,没抢到
    6号傻逼,没抢到
    9号傻逼,没抢到
    抢票示例

    (2)信号量:    (Semaphore)

    from multiprocessing import Process,Semaphore    ##  信号量  ##
    import time
    
    def dabaojian(i,s):
        s.acquire()
        print('%s号来洗脚'%i)
        time.sleep(1)
        s.release()
    
    if __name__ == '__main__':
        s = Semaphore(4)
        for i in range(10):
            p = Process(target=dabaojian,args=(i,s,))
            p.start()
    
    #########
    5号来洗脚
    0号来洗脚
    1号来洗脚
    4号来洗脚
    
    2号来洗脚
    3号来洗脚
    7号来洗脚
    6号来洗脚
    
    8号来洗脚
    9号来洗脚
    
    Process finished with exit code 0
    洗脚示例

    (3)事件: 事件完成红绿灯示例    (Event)

    from multiprocessing import Process,Event     ####  事件  ####
    import time
    
    
    def taffic_lights(e):
        while 1:
            print('红灯亮')
            time.sleep(5)
    
            e.set()
            print('绿灯亮')
            time.sleep(3)
            e.clear()
    
    def car(i,e):
    
            if not e.is_set():
                print('我们在等待。。。')
                e.wait()
                print('走你')
            else:
                print('可以走了。。')
    
    
    if __name__ =='__main__':
        e = Event()
        hld = Process(target=taffic_lights,args=(e,))
        hld.start()
    
        while 1:
            time.sleep(0.5)
    
            for i in range(3):
                p = Process(target=car,args=(i,e,))
                p.start()
    
    #################################
    红灯亮
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    我们在等待。。。
    绿灯亮
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    走你
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    可以走了。。
    红灯亮
    事件模拟完成红绿灯示例

    (4)队列:进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的。队列就像一个特殊的列表,但是可以设置固定长度,并且从前面插入数据,从后面取出数据,先进先出。

    Queue([maxsize]) 创建共享的进程队列。
    参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
    底层队列使用管道和锁实现。

    queue的方法介绍:

    q = Queue([maxsize]) 
    创建共享的进程队列。maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。底层队列使用管道和锁定实现。另外,还需要运行支持线程以便队列中的数据传输到底层管道中。 
    Queue的实例q具有以下方法:
    
    q.get( [ block [ ,timeout ] ] ) 
    返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True. 如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。
    
    q.get_nowait( ) 
    同q.get(False)方法。
    
    q.put(item [, block [,timeout ] ] ) 
    将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)。timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。
    
    q.qsize() 
    返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。
    
    
    q.empty() 
    如果调用此方法时 q为空,返回True。如果其他进程或线程正在往队列中添加项目,结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。
    
    q.full() 
    如果q已满,返回为True. 由于线程的存在,结果也可能是不可靠的(参考q.empty()方法)。。

    代码示例:

    from multiprocessing import Process,Queue         #####  队列  #####
    
    q = Queue(3)
    
    q.put(1)
    print(q.full())
    q.put(2)
    q.put(3)
    print(q.full())
    
    print(q.get())
    print(q.empty())
    print(q.get())
    print(q.get())
    print(q.empty())
    print(q.get())
    
    while 1:
        try:
            q.get(False)
            # q.get_nowait()
        except:
            print('队列目前为空')
    
    ##########################
    False
    True
    1
    False
    2
    3
    True
    队列的简单示例

    子进程和父进程通过队列进行通信

    from multiprocessing import Process,Queue
    import time
    
    def girl(q):
        print('来自boy的信息',q.get())
        print('来自领导的凝视',q.get())
    
    def boy(q):
        q.put('约么?')
    
    
    if __name__ == '__main__':
        q = Queue(5)
        boy_p = Process(target=boy,args=(q,))
        girl_p = Process(target=girl,args=(q,))
        boy_p.start()
        girl_p.start()
        time.sleep(1)
        q.put('好好工作,别乱搞')
    
    ##########################
    来自boy的信息 约么?
    来自领导的凝视 好好工作,别乱搞
    子进程与父进程通过队列进行通信

    生产者消费者模式:

    什么是生产者消费者模式:

      生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力,并且我可以根据生产速度和消费速度来均衡一下多少个生产者可以为多少个消费者提供足够的服务,就可以开多进程等等,而这些进程都是到阻塞队列或者说是缓冲区中去获取或者添加数据。

    示例:    主进程在生产者生产完毕后发送结束信号None

    from multiprocessing import Process,Queue
    import time
    
    def shengchan(q):
        for i in range(1,11):
            time.sleep(1)
            print('生产了%s号包子'%i)
            q.put(i)
    
    def xiaofei(q):
        while 1:
            time.sleep(2)
            s =  q.get()
            if s == None:
                break
            else:
                print('消费者吃了%s包子'%s)
     if __name__ =='__main__':
        q = Queue(20)
        sheng_p = Process(target=shengchan,args=(q,))
        sheng_p.start()
        xiao_p = Process(target=xiaofei,args=(q,))
        xiao_p.start()
        sheng_p.join()
    
        q.put(None)
    
    ############################
    生产了包子1号
    消费者吃了1包子
    生产了包子2号
    生产了包子3号
    消费者吃了2包子
    生产了包子4号
    生产了包子5号
    消费者吃了3包子
    生产了包子6号
    生产了包子7号
    消费者吃了4包子
    生产了包子8号
    生产了包子9号
    消费者吃了5包子
    生产了包子10号
    消费者吃了6包子
    消费者吃了7包子
    消费者吃了8包子
    消费者吃了9包子
    消费者吃了10包子
    
    Process finished with exit code 0

    JoinableQueue([maxsize]) 

    #JoinableQueue([maxsize]):这就像是一个Queue对象,但队列允许项目的使用者通知生成者项目已经被成功处理。通知进程是使用共享的信号和条件变量来实现的。
    
       #参数介绍:
        maxsize是队列中允许最大项数,省略则无大小限制。    
      #方法介绍:
        JoinableQueue的实例p除了与Queue对象相同的方法之外还具有:
        q.task_done():使用者使用此方法发出信号,表示q.get()的返回项目已经被处理。如果调用此方法的次数大于从队列中删除项目的数量,将引发ValueError异常
        q.join():生产者调用此方法进行阻塞,直到队列中所有的项目均被处理。阻塞将持续到队列中的每个项目均调用q.task_done()方法为止,也就是队列中的数据全部被get拿走了。

    示例:JoinableQueue队列实现生产者消费者模型

    import time
    from multiprocessing import Process,Queue,JoinableQueue
    
    def producer(q):
        for i in range(1,11):
            time.sleep(0.5)
            print('生产了包子%s号' % i)
            q.put(i)
        q.join()
        print('在这里等你')
    def consumer(q):
        while 1:
            time.sleep(1)
            s = q.get()
            print('消费者吃了%s包子' % s)
            q.task_done()  #给q对象发送一个任务结束的信号
    
    if __name__ == '__main__':
        #通过队列来模拟缓冲区,大小设置为20
        q = JoinableQueue(20)
        #生产者进程
        pro_p = Process(target=producer,args=(q,))
        pro_p.start()
        #消费者进程
        con_p = Process(target=consumer,args=(q,))
        con_p.daemon = True #
        con_p.start()
        pro_p.join()
        print('主进程结束')
    
    ##########################
    生产了包子1号
    生产了包子2号
    消费者吃了1包子
    生产了包子3号
    生产了包子4号
    消费者吃了2包子
    生产了包子5号
    生产了包子6号
    消费者吃了3包子
    生产了包子7号
    消费者吃了4包子
    生产了包子8号
    生产了包子9号
    消费者吃了5包子
    生产了包子10号
    消费者吃了6包子
    消费者吃了7包子
    消费者吃了8包子
    消费者吃了9包子
    消费者吃了10包子
    在这里等你
    主进程结束
    
    Process finished with exit code 0
    JoinableQueue队列实现生产者消费者模型

    逻辑思维如下图:

     

  • 相关阅读:
    token_get_all()函数
    sql_autoload_register()函数
    微信支付,使用证书时出现58错误
    $GLOBALS['HTTP_RAW_POST_DATA']与$_POST的区别
    mysql锁机制(转载)
    文件锁使用
    单行代码实现xml转换成数组
    Eclipse 安装 SVN 插件的两种方法
    python nltk 安装及配置说明
    HanLP自然语言处理包介绍
  • 原文地址:https://www.cnblogs.com/qq1426794755/p/9844596.html
Copyright © 2011-2022 走看看