• 生产者消费者模型实现多线程异步交互


    【Python之旅】第六篇(五):生产者消费者模型实现多线程异步交互

     

    消息队列 生产者消费者模型 多线程异步交互

    摘要:  虽然标题是“生产者消费者模型实现多线程异步交互”,但这里要说的应该还包括Python的消息队列,因为这里多线程异步交互是通过Python的消息队列来实现的,因此主要内容如下: 1 2 3 4 1.生产者消费者模型:厨师做包子与顾客吃包子 2.Python的消息队列 3.利用...

     虽然标题是“生产者消费者模型实现多线程异步交互”,但这里要说的应该还包括Python的消息队列,因为这里多线程异步交互是通过Python的消息队列来实现的,因此主要内容如下:

    1
    2
    3
    4
    1.生产者消费者模型:厨师做包子与顾客吃包子
    2.Python的消息队列
    3.利用消息队列实现Python多线程异步交互
    4.再谈耦合度的问题

    1.生产者消费者模型

        通过厨师做包子与顾客吃包子来引出生产者消费者模型,如下图:

    wKioL1YaZuXD7ML-AABsJiq32LM738.jpg

        这里,厨师相当于生产者,顾客相当于消费者,顾客吃包子,厨师做包子。做一个假设,如果厨师做包子的速度远远比顾客吃包子的速度要快,有这样一种情况,厨师等顾客吃完一个包子后再做下一个包子(这时我们说厨师与顾客的耦合度高,即厨师做包子与顾客吃包子是紧密相连的),这样显然效率会很低,现实情况中也不可能是这样,因为一旦顾客多时,厨师就有可能忙不过来了。

        可以尝试这样解决:不管顾客有没有吃完包子,厨师也继续做包子,但此时是先把包子放在一个包子柜台中,等顾客有需要时再去包子柜台拿包子。如此一来,厨师和顾客的耦合度就变低了,即厨师做包子并不取决于顾客是否把包子吃完,这样的话效率显然就会高多。

    2.Python中的消息队列

        通过上面的例子中,就可以引出消息队列了:包子柜台即相当于Python中的消息队列(当然不只有Python才有消息队列)。根据上面的例子,我们做下面的类比分析。

        类比分析1:厨师和包子相当于是两个线程(假设线程A和线程B),厨师做的包子即相当于是线程A执行后的结果,而线程B的执行需要利用线程A的执行结果,并且,线程A的执行速度比线程B的执行速度要快。

        类比分析2:厨师不会等顾客吃完包子后再做下一个包子,即线程A也不会等线程B使用线程A的执行结果后再去执行下一次功能相同的线程A2,否则程序运行效率会很低。

        类比分析3:厨师把做好的包子放在包子柜台里,顾客吃完一个包子后再去包子柜台取,线程A把执行结果存放在消息队列中,然后再执行下一个功能相同的线程A2,线程B在消息队列中取胜线程A的执行结果,然后再执行下一个功能相同的线程B2,如此类推。

        通过上面的分析,我们就可以知道,通过使用消息队列,我们就可以降低两个线程之间的耦合度,这样就可以提高程序的运行效率。

    3.利用消息队列实现Python多线程异步交互

        上面的例子,线程A和线程B的执行速度是不一样的(异步),但却线程B却需要使用线程A的执行结果(交互),通过使用Python的消息队列,就可以实现线程的异步交互。

        程序代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    #!/usr/bin/env python
     
     
    import threading, time
    import Queue    #导入消息队列模块
    import random   #导入随机数模块,是为了模拟生产者与消费者速度不一致的情形
    q = Queue.Queue()    #实例化一个对象
     
    def Producer(name):          #生产者函数
        for in range(20):  
            q.put(i)     #将结果放入消息队列中
            print '33[32;1mProducer %s has made %s baozi....33[0m' % (name, i)
            time.sleep(random.randrange(3))    #生产者的生产速度,3s内
    def Consumer(name):          #消费者函数
        count = 0
        while count < 20:
            data = q.get()    #取用消息队列中存放的结果
            print '33[31;1mConsumer %s has eatem %s baozi...chihuo...33[0m' % (name, data)
            count += 1
            time.sleep(random.randrange(4))    #消费者的消费速度,4s内
     
    p = threading.Thread(target = Producer, args = ('Alex',))
    c = threading.Thread(target = Consumer, args = ('Wangfanhao',))
     
    p.start()
    c.start()

        程序执行结果如下:

    wKiom1YabuuxTmHHAAP7wIDR7v4163.jpg

    wKioL1YabwrgL24XAANaB-1ZI3c686.jpg

        利用这个程序,很好地模拟了前面“厨师做包子顾客吃包子”的例子,而从程序的执行结果中也可以看出,线程的执行是异步的,尽管如此,数据还是进行了交互,作用是:在多线程和多线程之间进行数据交互的时候,不会出现数据的阻塞。

        基于前面3点,对Python多线程异步交互应该是有一个比较好的理解了。

    4.再谈耦合度的问题

        继续前面“厨师做包子与顾客吃包子”的问题,有一个问题,如果包子的制作是两个厨师完成,即一个做馅,一个包包子,那么正常情况下,两个人的工作需要串行完成才能做完一个包子,即这两个人的耦合度非常高,也就是说他们的工作联系紧密,在这种情况下,我们应该尽可能地减少这种耦合度,这时,只需要在两个厨师之间再加一个队列,即厨师A把馅做好后就放在队列中,厨师B根据自己做包子的快慢去取馅,这样就不会出现厨师A非得等厨师B把一个包子包完了再去做另一个包子的馅,厨师B也不一定需要先等厨师A把手头上的馅做好才去包包子,因为这时队列中已经有馅了,如此因为,再根据两个两项工作速度的不同,给厨师A或厨师B再多增加一名或多名助手,就可以大大地增加整一个做包子的速度了。

        运用在写程序的过程中,也应该要这样去思考。

  • 相关阅读:
    php 0916
    心里话
    LibreOJ 6559 小奇采药
    计蒜客 T1658 热浪
    计蒜客 T2021 飞扬的小鸟
    POJ 1276 Cash Machine
    SCU 3366 Watering Hole
    POJ 3268 Silver Cow Party
    luogu P4568 [JLOI2011]飞行路线
    POJ 1179 Polygon
  • 原文地址:https://www.cnblogs.com/weiman3389/p/6044938.html
Copyright © 2020-2023  润新知