• 【Python爬虫学习笔记10】多线程中的生产者消费者模式


    在多线程编程中,最经典的模式是生产者消费者模式。其中,生产者是专门用来生产数据的线程,它把数据存放在一个中间变量中;而消费者则从这个中间变量取出数据进行消费。由于生产者和消费者共享中间变量,这些变量大多是全局的,因此需要使用锁来保证数据完整性,防止多线程共享问题的产生。

    Python threading模块为我们提供了两种实现生产者消费者模式的方案,分别基于Lock类和Condition类。在具体介绍前,我们先来说明一下我们的生产者消费者模型背景:我们这里有一个金库gMoney,若干个生产者Producer和消费者Consumer,其中生产者负责向金库里存钱,而消费者则从金库中取钱;当生产者存放10次钱以后则不再存钱,而消费者所取的钱只要没有超过金库剩余便可一直取。

    接下来我们我们对两种方案的实现进行介绍。

    使用Lock类实现

    Lock类实现方案采用的是threading中的锁机制,通过对共享变量操作加锁和释放锁来确保数据的统一性,具体程序如下:

    ## 使用Lock类实现生产者消费者模式
    import threading
    import random,time
    
    gMoney = 1000  # 定义全局金库、
    gLock = threading.Lock()  # 实例化对金库操作的锁
    gTotaltimes = 10  # 存钱总次数和
    gTimes = 0     # 存钱当前次数
    
    
    # 生产者线程类
    class Producer(threading.Thread):
        def run(self):
            global gMoney, gTimes
            while True:  # 循环存钱操作
                if gTimes >= gTotaltimes: return  # 当存钱次数上限时结束线程
                money = random.randint(100, 1000)  # 存钱数
                gLock.acquire()  # 对金库操作加锁
                gMoney += money
                print('%s produces $%s,the balance is %s .' % (threading.current_thread(), money, gMoney))
                gTimes += 1
                gLock.release()  # 对金库操作释放锁
                time.sleep(0.5)
    
    
    # 消费者线程类
    class Consumer(threading.Thread):
        def run(self):
            global gMoney
            while True:  # 循环取钱操作
                money = random.randint(100, 1000)  # 取钱数
                if gMoney >= money:  # 金库钱充足
                    gLock.acquire()  # 对金库操作加锁
                    gMoney -= money
                    print('%s consumes $%s,the balance is %s .' % (threading.current_thread(), money, gMoney))
                    gLock.release()  # 对金库操作释放锁
                    time.sleep(0.5)
                else:  # 金库钱不足
                    if gTimes >= gTotaltimes:  # 若已达存钱次数上限,则退出
                        return
                    print('%s wants to consume $%s,while the balance is %s .' % (threading.current_thread(), money, gMoney))
    
    
    # 开启两个生产者线程和1个消费者线程
    def main():
        for x in range(2):
            t = Producer()
            t.start()
        for x in range(1):
            t = Consumer()
            t.start()
    
    
    if __name__ == '__main__':
        main()

    使用Condition类实现

    在threading模块中的另一个较优的解决方案是使用Condition类,其可以在没有数据的时候自动地处于阻塞等待状态,而一旦有合适的数据则可以使用notify相关函数来通知处于等待状态的线程(此时并不会使其直接执行)。这样一来就可以不用做一些无用的上锁和解锁操作,提高程序的性能。

    在实现生产者消费者模式前,先来说明一下Condition类的几个常用方法:

    1.acquire():上锁。
    2.release():释放锁。
    3.wait():将当前线程处于等待状态,并且会自动释放其已占有的锁。该线程可以被其他线程使用notify()和notify_all()唤醒,被唤醒后会继续自动等待上锁,上锁后再执行后续代码。
    4.notify()/notify_all():通知某个/所有正在等待的线程。这两个方法都不会释放锁,并且需要在release()之前调用。

    和Lock方案类似,仅做了少数地方的修改,具体实现如下:

    ## 使用Condition类实现生产者消费者模式
    import threading
    import random, time
    
    gMoney = 1000
    gCondition = threading.Condition()  # 实例化Condition类
    gTotalTimes = 10
    gTimes = 0
    
    
    # 生产者线程类
    class Producer(threading.Thread):
        def run(self):
            global gMoney, gTimes
            while True:
                if gTimes >= gTotalTimes: return
                money = random.randint(100, 1000)
                gCondition.acquire()  # 加锁
                gMoney += money
                print('%s produces $%s,the balance is %s .' % (threading.current_thread(), money, gMoney))
                gTimes += 1
                gCondition.release()  # 释放锁
                time.sleep(0.5)
    
    
    # 消费者线程类
    class Consumer(threading.Thread):
        def run(self):
            global gMoney
            while True:
                money = random.randint(100, 1000)
                gCondition.acquire()  # 加锁
                while gMoney < money:  # 金库不足
                    if gTimes >= gTotalTimes:
                        gCondition.release()  # 如果达到存钱次数上限则释放锁并退出
                        return
                    print('%s wants to consume $%s,while the balance is %s .' % (threading.current_thread(), money, gMoney))
                    gCondition.wait()  # 不足情况下进入等待
                gMoney -= money
                print('%s consumes $%s,the balance is %s .' % (threading.current_thread(), money, gMoney))
                gCondition.release()  # 释放锁
                time.sleep(0.5)
    
    
    # 开启线程
    def main():
        for x in range(2):
            t = Producer()
            t.start()
        for x in range(1):
            t = Consumer()
            t.start()
    
    
    if __name__ == '__main__':
        main()

  • 相关阅读:
    wp8使用mvvm模式简单例子(二)---登陆功能,事件触发
    wp8使用mvvm模式简单例子
    win8.1使用WP8SDK出现Windows Phone Emulator无法启动的问题解决方案
    asp.net原理笔记----页面控件类型,页面状况和asp.net编译过程
    asp.net生命周期
    asp.net服务器数据源控件学习笔记
    AJax学习笔记
    asp.net敏感词过滤
    网上书城总结笔记
    在自己的网站上使用RSS订阅功能
  • 原文地址:https://www.cnblogs.com/ChanKaion/p/9704207.html
Copyright © 2020-2023  润新知