网络编程八:线程间的同步之condition通知等待

一、condition:通知等待

有一类线程需要满足条件之后才能够继续执行。

condition条件变量,除了提供RLock()或Lock()的方法(如acquire,release)外,还提供了 wait()、notify()、notifyAll()方法。

acquire():加锁

release():解锁

通常使用with condition代替加锁、解锁

 

wait([timeout]):阻塞线程,线程挂起,直到收到一个notify通知或者超时(可选的,浮点数,单位是秒s)才会被唤醒继续运行。

wait()必须在加锁前提下才能调用,否则会触发RuntimeError。

调用wait()会释放锁,直至该线程被Notify()、NotifyAll()或者超时,线程又重新获得锁。

 

notify(n=1):通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。

notify()必须在加锁前提下才能调用,否则会触发RuntimeError。

notify()不会主动释放锁。

notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程(这个一般用得少)

lock_con=threading.Condition([Lock/Rlock])  # 锁是可选选项,默认创建一个RLock(), 一般都用默认。

 

 二、示例:

生产者与消费者:使用event实现,1秒钟生产1条数据,消费1条数据

import threading, logging, random
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(threadName)s -- %(message)s')
class Dispatcher:
    def __init__(self):
        self.data = None
        self.event = threading.Event()
    def consumer(self):
        while not self.event.wait(1):
            logging.info("消费了数据{}".format(self.data))
    def producer(self):
        for _ in range(10):
            data = random.randint(1, 100)
            logging.info("生产了数据{}".format(data))
            self.data = data
            self.event.wait(1)
        self.event.set()
d = Dispatcher()
p = threading.Thread(target=d.producer, name='producer')
c = threading.Thread(target=d.consumer, name='consumer')
c.start()
p.start()

 

 

2018-11-04 16:01:31,980 INFO producer -- 生产了数据92
2018-11-04 16:01:32,994 INFO consumer -- 消费了数据92
2018-11-04 16:01:33,004 INFO producer -- 生产了数据86
2018-11-04 16:01:34,008 INFO consumer -- 消费了数据86

 

缺点:消费者使用了等1秒消费1条数据,是同生产者的巧合。

正确的做法:生产者生产数据后,通知消费者。使用condition通知消费者。

增加:1个生产者,生产数据后,通知4个消费者消费。订阅--分发。

import threading, logging, random
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(levelname)s %(threadName)s -- %(message)s')
class Dispatcher:
    def __init__(self):
        self.data = None
        self.event = threading.Event()
        self.cond = threading.Condition()
    def consumer(self):
        while not self.event.is_set():
            with self.cond:
                self.cond.wait()
                logging.info("消费了数据{}".format(self.data))
    def producer(self):
        for _ in range(10):
            data = random.randint(1, 100)
            logging.info("生产了数据{}".format(data))
            self.data = data
            with self.cond:
                self.cond.notifyAll()
            self.event.wait(1)
        self.event.set()
d = Dispatcher()
p = threading.Thread(target=d.producer, name='producer')
for i in range(4):
    c = threading.Thread(target=d.consumer, name='consumer')
    c.start()
p.start()
2018-11-04 16:15:24,170 INFO producer -- 生产了数据94
2018-11-04 16:15:24,170 INFO consumer -- 消费了数据94
2018-11-04 16:15:24,170 INFO consumer -- 消费了数据94
2018-11-04 16:15:24,170 INFO consumer -- 消费了数据94
2018-11-04 16:15:24,170 INFO consumer -- 消费了数据94

 

cond.notifyAll():通知所有消费者

cond.notify(num):通知num个消费者,num默认为1

            with self.cond:
                self.cond.notify(2)
2018-11-04 16:22:18,250 INFO producer -- 生产了数据40
2018-11-04 16:22:18,250 INFO consumer -- 消费了数据40
2018-11-04 16:22:18,250 INFO consumer -- 消费了数据40
2018-11-04 16:22:19,272 INFO producer -- 生产了数据41
2018-11-04 16:22:19,272 INFO consumer -- 消费了数据41
2018-11-04 16:22:19,272 INFO consumer -- 消费了数据41

 

 

Condition:通常用于生产者消费者模式,生产者生产消息之后,使用notify或者notify_all通知消费者消费。

cond.wait():消费者用cond.wait()方法阻塞,等待生产者的通知。

无论是cond.wait,还是cond.notify,cond.notify_all方法,都必须在获取Lock/RLock的前提下,才可以被调用。

通常的做法,是在cond.wait,cond.notify,cond.notify_all方法前,调用with self.cond。

 

三、condition与队列的区别

使用队列实现的生产者消费者模式,是单播模式。

使用condition的notify(num)或notify_all()实现的生产者消费者模式,可以是广播的模式。

 

 

 

 

 

 

 

 

 

 

 

 

 













posted on 2018-11-04 15:13  myworldworld  阅读(170)  评论(0)    收藏  举报

导航