• [Python 多线程] Condition (十)


    Condition常用于生产者、消费者模型,为了解决生产者消费者速度匹配问题。

    构造方法Condition(lock=None),可以传入一个Lock或RLock对象,默认RLock。

    方法:

    acquire(*args)  获取锁

    release()     释放锁

    wait(timeout=None)  等待通知或直到发生超时

    notify(n=1)  唤醒至多指定个数的等待的线程,没有等待的线程就没有任何操作

    notify_all()  唤醒所有等待的线程。wake up

    以下例子,不考虑线程安全问题:

    例1:

    #Condition
    import threading,random,logging
    logging.basicConfig(level=logging.INFO)
    
    class Dispatcher:
        def __init__(self):
            self.data = 0
            self.event = threading.Event()
    
        def produce(self):
            for i in range(100):
                self.event.wait(1) #每1秒生成一条数据
                data = random.randint(1,100)
                self.data = data
    
        def custom(self):
            while True:
                logging.info(self.data)
                self.event.wait(0.5) # 替换为1表示消费者每1秒取一次数据
    
    
    d = Dispatcher()
    p = threading.Thread(target=d.produce)
    c = threading.Thread(target=d.custom)
    
    c.start()
    p.start()
    
    以下结果:
    INFO:root:0
    INFO:root:0
    INFO:root:0
    INFO:root:77
    INFO:root:64
    INFO:root:64
    INFO:root:8
    INFO:root:8
    INFO:root:85
    

      生产者每1秒钟生成一条数据,消费者每0.5秒就来取一次数据。

    例2:

    #Condition 通知机制,解决重复
    import threading,random,logging
    logging.basicConfig(level=logging.INFO)
    
    class Dispatcher:
        def __init__(self):
            self.data = 0
            self.event = threading.Event()
            self.cond = threading.Condition()
    
        def produce(self):
            for i in range(100):
                data = random.randint(1,100)
                with self.cond:
                    self.data = data
                    self.cond.notify_all() #通知所有waiter
                self.event.wait(1) #1秒生产一次数据
    
        def custom(self):
            while True:
                with self.cond:
                    self.cond.wait() #无限等待
                    logging.info(self.data) #消费
    
                self.event.wait(0.5) #0.5秒消费一次数据
    
    
    d = Dispatcher()
    p = threading.Thread(target=d.produce)
    c = threading.Thread(target=d.custom)
    
    c.start()
    p.start()
    
    运行结果:
    INFO:root:64
    INFO:root:43
    INFO:root:11
    INFO:root:28
    INFO:root:30
    INFO:root:33
    INFO:root:93
    INFO:root:69
    INFO:root:4
    

      使用with来管理Condition的上下文(acquire/release),利用Condition通知机制解决消费者获取重复数据。

    例3:

    #Condition 先生成后消费,1对1
    import threading,random,logging
    logging.basicConfig(level=logging.INFO,format="%(thread)d %(threadName)s %(message)s")
    
    class Dispatcher:
        def __init__(self):
            self.data = 0
            self.event = threading.Event()
            self.cond = threading.Condition()
    
        def produce(self):
            for i in range(100):
                data = random.randint(1,100)
                logging.info(self.data)
                with self.cond:
                    self.data = data
                    self.cond.notify(1)
                    # self.cond.notify_all()
                self.event.wait(1)
    
        def custom(self):
            while True:
                with self.cond:
                    self.cond.wait()
                    logging.info(self.data)
    
                self.event.wait(0.5)
    
    
    d = Dispatcher()
    p = threading.Thread(target=d.produce,name='produce')
    c = threading.Thread(target=d.custom,name='c')
    c1 = threading.Thread(target=d.custom,name='c1')
    p.start()
    
    e = threading.Event()
    e.wait(3)
    
    c1.start()
    c.start()
    
    运行结果:
    7520 produce 0
    7520 produce 78
    7520 produce 88
    7520 produce 14
    7520 produce 83
    2508 c1 86
    7520 produce 86
    1136 c 79
    7520 produce 79
    2508 c1 77
    7520 produce 77
    1136 c 47
    7520 produce 47
    2508 c1 76
    7520 produce 76
    1136 c 69
    

      生产者先生产数据,2个消费者一个一个来消费数据。

    例4:

    #Condition 1对多,2个2个通知
    import threading,random,logging
    logging.basicConfig(level=logging.INFO,format="%(thread)d %(threadName)s %(message)s")
    
    class Dispatcher:
        def __init__(self):
            self.data = 0
            self.event = threading.Event()
            self.cond = threading.Condition()
    
        def produce(self):
            for i in range(100):
                data = random.randint(1,100)
                logging.info(self.data)
                with self.cond:
                    self.data = data
                    self.cond.notify(2)
                    # self.cond.notify_all()
                self.event.wait(1)
    
        def custom(self):
            while True:
                with self.cond:
                    self.cond.wait()
                    logging.info(self.data)
                # self.event.wait(0.5)
    
    
    d = Dispatcher()
    p = threading.Thread(target=d.produce,name='produce')
    
    for i in range(5):
        threading.Thread(target=d.custom,name='c-{}'.format(i)).start()
    
    p.start()
    
    以下结果:
    8688 produce 0
    10376 c-0 90
    7928 c-1 90
    8688 produce 90
    7640 c-2 61
    10748 c-3 61
    8688 produce 61
    10376 c-0 73
    1344 c-4 73
    8688 produce 73
    7928 c-1 57
    7640 c-2 57
    8688 produce 57
    10748 c-3 71
    10376 c-0 71
    

      1对多,2个2个通知来处理数据。

    以上例子中,程序本身不是线程安全的,程序逻辑有很多瑕疵,但是可以很好的帮助理解Condition的使用,和生产者消费者模型。

    Condition总结:

    Condition采用通知机制,常用于生产者消费者模型中,解决生产者消费者速度匹配的问题。

    使用方法:

    使用Condition,必须先acquire,用完之后要release,因为内部使用了锁,默认使用RLock,最好的方法是使用with上下文管理。

    生产者wait,会阻塞等待通知,被激活。

    生产者生产好消息,对消费者发通知,可以使用notidy_all() 通知所有消费者或者notify()。

  • 相关阅读:
    设置DELL R720 的CPU 风扇转速
    mysqldump 备份导出数据排除某张表或多张表
    MySQL 之 LOAD DATA INFILE 快速导入数据 (单表数据很大)
    ACL规则 反掩码的 写法
    配置Nginx 支持中文URL
    如何删除IE中的证书
    微软Surface 上网本 键盘失灵
    华为 S2700忘记console口密码
    Dell PowerEdge R710, R720 用U盘装系统
    用php做省份的三级联动 附带数据库
  • 原文地址:https://www.cnblogs.com/i-honey/p/8068195.html
Copyright © 2020-2023  润新知