网络编程八:线程间的同步之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) 收藏 举报