【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()

其中一次运行结果如下:

posted @ 2020-04-04 16:40  OLIVER_QIN  阅读(543)  评论(0编辑  收藏  举报