• 【Python3 爬虫】U26_多线程爬虫之生产者与消费者模式



    生产者消费者模型是多线程开发中经常见到的一种模式。生产者专门生产一些数据,把这些数据放到变量中,消费者从变量中取出一些数据来消费。由于要使用到中间变量,这些变量一般是全局变量,所以使用使用锁机制来保证数据的完整性。就好比老爸去工作赚钱存在银行,儿子从银行取钱来花,老爸是生产者,银行就是中间变量,儿子是消费者。

    1.Lock版生产者与消费者模型

    import time
    import threading
    import random
    
    g_money = 1000
    g_lock = threading.Lock()
    g_total_times = 10
    g_times = 0
    
    
    class Producer(threading.Thread):
        def run(self):
            global g_money
            global g_times
            while True:
                money = random.randint(100,1000)
                g_lock.acquire()
                if g_times >= g_total_times: # 生产者生产10次就停止
                    g_lock.release()
                    break
                g_money += money
                print('%s生产者生产了%d元,剩余%d元' %(threading.current_thread(),money,g_money))
                g_times += 1
                g_lock.release()
                time.sleep(1)
    
    class Consumer(threading.Thread):
        def run(self):
            global g_money
            global g_times
            while True:
                money = random.randint(100, 1000)
                g_lock.acquire()
                if g_money >= money:  # 当剩余的钱大于消费者需要消费的金额的时候,才可以消费
                    g_money -= money
                    print('%s消费者消费了%d元,剩余%d元' % (threading.current_thread(), money, g_money))
                else:
                    # 在if条件中已经判断金钱是否够消费,此处还需要判断,如果生产者生产的次数已经完了,那么消费者也别在这干等了,没有钱会被生产了
                    if g_times >= g_total_times:
                        g_lock.release()
                        break
                    print("%s消费者准备消费%d元,剩余%d元,余额不足了!你还消费个锤子!" %(threading.current_thread(), money, g_money))
                g_lock.release()
                time.sleep(1)
    
    def main():
        for i in range(3):
            t = Producer()
            t.start()
    
        for i in range(4):
            t = Consumer()
            t.start()
    
    if __name__ == '__main__':
        main()
    

    以下是其中一次运行结果截图:

    2.condition版的生产者与消费者模型

    Lock版本的生产者与消费者模型可以正常运行,但是存在一个缺点,在消费者中,总是while True死循环上锁的方式去判断钱够不够,这样很耗费资源的。上锁本身就比较耗费CPU资源,因此这种实现是不理想的。那么就可以使用threading.condition来实现。threading.condition在没有数据的时候处于阻塞等待状态。一旦有合适的数据了,还可以使用notify相关的函数来通知其他处于等待状态的线程。这样就可以少做一些无用的上锁和解锁操作,提高程序性能。
    以下是threading.condition常用函数介绍:

    • acquire:上锁
    • release:解锁
    • wait:将线程处于等待状态,并且会释放锁。可以被其他线程使用notifynotify_all函数唤醒。被唤醒后会继续等待上锁,上锁后继续还行下面的代码。
    • notify:通知某个正在等待的线程,默认是第一个线程。
    • notify_all:通知正在等待的线程。notify不会与notify_all不会释放锁,需要在release之前调用。

    示例代码如下:

    # Author:Logan
    import time
    import threading
    import random
    
    g_money = 1000
    g_condition = threading.Condition()
    g_total_times = 10
    g_times = 0
    
    
    class Producer(threading.Thread):
        def run(self):
            global g_money
            global g_times
            while True:
                money = random.randint(100,1000)
                g_condition.acquire()
                if g_times >= g_total_times: # 生产者生产10次就停止
                    g_condition.release()
                    break
                g_money += money
                print('%s生产者生产了%d元,剩余%d元' %(threading.current_thread(),money,g_money))
                g_times += 1
                g_condition.notify_all()
                g_condition.release()
                time.sleep(1)
    
    class Consumer(threading.Thread):
        def run(self):
            global g_money
            global g_times
            while True:
                money = random.randint(100, 1000)
                g_condition.acquire()
                while g_money < money:  # 当消费的钱大于现有的钱,并且生产者还在生产钱的情况下,则处于等待
                    if g_times >= g_total_times:
                        g_condition.release()
                        return
                    print("%s消费者准备消费%d元,剩余%d元,余额不足了!你还消费个锤子!" % (threading.current_thread(), money, g_money))
                    g_condition.wait()
                g_money -= money
                print('%s消费者消费了%d元,剩余%d元' % (threading.current_thread(), money, g_money))
                g_condition.release()
                time.sleep(1)
    
    def main():
        for i in range(3):
            t = Producer()
            t.start()
    
        for i in range(4):
            t = Consumer()
            t.start()
    
    if __name__ == '__main__':
        main()
    

    其中一次运行结果如下:

  • 相关阅读:
    php yield
    原来 php 中的 json_encode() 只支持utf-8.不支持gbk啊
    mongodb 二进制安装
    Centos 6.3 安装教程
    php 测试 程序执行时间,内存使用情况
    workerman vmstat服务器状态监控服务
    php大量数据 10M数据从查询到下载 【内存溢出,查询过慢】解决方案
    PHP_EOL DIRECTORY_SEPARATOR
    利用curl 模拟多线程
    Laravel 输出最后一条sql
  • 原文地址:https://www.cnblogs.com/OliverQin/p/12632451.html
Copyright © 2020-2023  润新知