python 不修改主线程 同步_Python线程同步(1)

本文介绍了Python线程同步的概念,通过Event事件对象实现线程间的简单通信,并通过示例展示了Event在生产者-消费者问题中的应用。接着讨论了锁(Lock)在多线程资源共享中的作用,防止数据不一致。最后,通过Condition对象演示了一对多的通知机制,解决生产者和消费者速度匹配问题。
摘要由CSDN通过智能技术生成

概念

线程同步,线程间协同,通过某种技术,让一个线程访问某些数据时,其他线程不能访问这些数据,直到该线程完成对数据的操作。

不同操作系统实现拘束有所不同,有临界区(Critical Section),互斥量(Mutex)、信号量(Semaphore)、事件event等。

Event(重要)

event事件,是线程间通信机制中最简单的实现,使用一个内部的标记flag,通过flag的true或false的变化来进行操作。

set():标记设置为true

clear():标记设置为false

is_set():标记是否为true

wait(timeout = none):设置等待标记为true的时长,none为无限等待,等到返回true,未等到超时了返回false.

需求

老板雇佣了一个工人,让他生产一个杯子,老板一直等着这个工人,知道生产了10个杯子。

from threading importEvent,Threadimportloggingimporttime

FORMAT= "%(asctime)s %(threadNname)s %(thread)d %(message)s"logging.basicConfig(format= FORMAT,level =logging.INFO)defboss(event:Event):

logging.info("i'm boss,waiting for u")#等待

event.wait()

logging.info("good job")def worker(event:Event,count = 10):

logging.info("i'm working for u")

cups=[]whileTrue:

logging.info("make 1")

time.sleep(0.5)

cups.append(1)if len(cups)>=count:#通知

event.set()breaklogging.info("i finished my job. cups = {}".format(cups))

event=Event()

w= Thread(target = worker,args =(event,))

b= Thread(target = boss,args =(event,))

w.start()

b.start()

结果为:

2019-11-25 17:13:57,577 Thread-1 9760 i'm working for u

2019-11-25 17:13:57,577 Thread-1 9760 make 1

2019-11-25 17:13:57,577 Thread-2 10592 i'm boss,waiting for u.

2019-11-25 17:13:58,077 Thread-1 9760 make 1

2019-11-25 17:13:58,577 Thread-1 9760 make 1

2019-11-25 17:13:59,077 Thread-1 9760 make 1

2019-11-25 17:13:59,577 Thread-1 9760 make 1

2019-11-25 17:14:00,077 Thread-1 9760 make 1

2019-11-25 17:14:00,577 Thread-1 9760 make 1

2019-11-25 17:14:01,077 Thread-1 9760 make 1

2019-11-25 17:14:01,577 Thread-1 9760 make 1

2019-11-25 17:14:02,077 Thread-1 9760 make 1

2019-11-25 17:14:02,577 Thread-1 9760 i finished my job. cups = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

2019-11-25 17:14:02,577 Thread-2 10592 good job

总结

使用同一个Event对象的标记flag,谁wait就是等到flag变为true,或等到超时返回false,不限制等待的个数。

wait的作用

from threading importEvent,Threadimportlogging

logging.basicConfig(level=logging.INFO)defdo(event:Event,interval:int):while not event.wait(interval):#条件中使用,返回true或false

logging.info("do sth")

e=Event()

Thread(target= do,args = (e,3)).start()

e.wait(10)#也可以使用time.sleep(10)

e.set()print("main exit")

Event的wait优于time.sleep,它会更快的切换到其他线程,提高并发效率。

Event练习

实现Timer,延时执行的线程

延时计算add(x,y)

思路

Timer的构造函数中参数得有哪些?

如何实现start启动一个线程执行函数

如何cancel取消待执行任务

思路实现

from threading importEvent,Threadimportlogging

logging.basicConfig(level=logging.INFO)defadd(x:int,y:int):

logging.info(x+y)classTimer():def __init__(self,interval,fn,*args,**kwargs):pass

defstart(self):pass

defcancel(self):pass

完整实现

from threading importEvent,Threadimportloggingimportdatetime

logging.basicConfig(level=logging.INFO)defadd(x:int,y:int):

logging.info(x+y)classTimer():def __init__(self,interval,fn,*args,**kwargs):

self.interval=interval

self.fn=fn

self.args=args

self.kwargs=kwargs

self.event=Event()defstart(self):

Thread(target= self.__run).start()defcancel(self):

self.event.set()def __run(self):

start=datetime.datetime.now()

logging.info("waiting")

self.event.wait(self.interval)if notself.event.is_set():

self.fn(*self.args,**self.kwargs)

delta= (datetime.datetime.now() -start).total_seconds()

logging.info("finished {}".format(delta))

self.event.set()

t= Timer(10,add,4,50)

t.start()

e=Event()

e.wait(4)

t.cancel()print("============")

或者

from threading importEvent,Threadimportloggingimportdatetime

logging.basicConfig(level=logging.INFO)defadd(x:int,y:int):

logging.info(x+y)classTimer():def __init__(self,interval,fn,*args,**kwargs):

self.interval=interval

self.fn=fn

self.args=args

self.kwargs=kwargs

self.event=Event()defstart(self):

Thread(target= self.__run).start()defcancel(self):

self.event.set()def __run(self):

start=datetime.datetime.now()

logging.info("waiting")if notself.event.wait(self.interval):

self.fn(*self.args,**self.kwargs)

delta= (datetime.datetime.now() -start).total_seconds()

logging.info("finished {}".format(delta))

self.event.set()

t= Timer(10,add,4,50)

t.start()

e=Event()

e.wait(4)

t.cancel()print("============")

lock

锁,凡是存在共享资源争抢的地方都可以使用锁,从而保证只有一个使用者可以完全使用这个资源。

需求:

订单要求生产1000个杯子, 组织10个工人生产。

importloggingimportthreading

logging.basicConfig(level=logging.INFO)#10个人生产100个杯子

cups=[]def worker(task = 100):whileTrue:

count=len(cups)

logging.info(count)if count>=task:breakcups.append(1)

logging.info("{} make 1".format(threading.current_thread().name))

logging.info("{}".format(cups))for _ in range(10):

threading.Thread(target= worker,args=(100,)).start()

从结果截图可以看出,最后的结果是104,并不是100,因为线程都在访问cups,也就是都在访问cups,这就导致了多生产。也就是接近临界点的时候,都在生产。

importthreadingfrom threading importThread,Lockimportloggingimporttime

FORMAT= "%(asctime)s %(threadName)s %(thread)d %(message)s"logging.basicConfig(format= FORMAT,level =logging.INFO)

cups=[]

FORMAT= "%(asctime)s %(threadName)s %(thread)d %(message)s"logging.basicConfig(format= FORMAT,level =logging.INFO)

cups=[]def worker(count = 10):

logging.info("i'm working for u")while len(cups)

time.sleep(0.0001)#为了看出线程切换效果

cups.append(1)

logging.info("i finished . cups = {}".format(len(cups)))for _ in range(10):

Thread(target= worker,args = (1000,)).start()

从上例的运行结果来看,多线程调度,导致了判断失效,多生产了杯子。如何修改?加锁

lock

锁,一旦线程获得锁,其他视图获取锁的线程将被阻塞。

importthreading

lock=threading.Lock()

lock.acquire()#拿到锁

print("get locker")

lock.release()print("release locker")

结果为:

get locker

release locker

上面拿到锁了,然后打印,再然后释放锁,再打印。

importthreading

lock=threading.Lock()

lock.acquire()#拿到锁

print("get locker1")

lock.acquire()print("get locker2")

lock.release()print("release locker")

结果为:

get locker1

上面拿到锁了,没有释放,又在继续拿锁,就被阻塞咯。等待释放。不能在往后面执行。

importloggingimportthreading

logging.basicConfig(level=logging.INFO)#10个人生产100个杯子

cups=[]

lock=threading.Lock()def worker(lock:threading.Lock ,task =100):whileTrue:

lock.acquire()

count=len(cups)

lock.release()

logging.info(count)if count>=task:breaklock.acquire()

cups.append(1)

lock.release()

logging.info("{} make 1".format(threading.current_thread().name))

logging.info("{}".format(cups))for _ in range(10):

threading.Thread(target= worker,args=(lock,100)).start()

上面这样的代码,最后的执行结果还是不正确,因为从业务上来说是不正确的。

importloggingimportthreading

logging.basicConfig(level=logging.INFO)#10个人生产100个杯子

cups=[]

lock=threading.Lock()def worker(lock:threading.Lock ,task =100):whileTrue:

lock.acquire()

count=len(cups)

logging.info(count)if count>=task:breakcups.append(1)

lock.release()

logging.info("{} make 1".format(threading.current_thread().name))

logging.info("{}".format(cups))for _ in range(10):

threading.Thread(target= worker,args=(lock,100)).start()

这样写才是正确的。但是效率还是不太高。还有点问题!因为到中间的位置,被break了,这个时候根本没有释放锁。

看结果,程序根本就没有结束。直接阻塞咯。

importloggingimportthreading

logging.basicConfig(level=logging.INFO)#10个人生产100个杯子

cups=[]

lock=threading.Lock()def worker(lock:threading.Lock ,task =100):whileTrue:

lock.acquire()

count=len(cups)

logging.info(count)if count>=task:

lock.release()breakcups.append(1)

lock.release()

logging.info("{} make 1".format(threading.current_thread().name))

logging.info("{}".format(cups))for _ in range(10):

threading.Thread(target= worker,args=(lock,100)).start()

这样才可以咯。

acquire(blocking = True,timeout = -1):默认阻塞,阻塞可以设置超时时间,非阻塞时,timeout禁止设置。成功获取锁,返回true,否则返回false。

release():释放锁。可以从任何线程调用释放,已上锁的锁,会被重置为unlocked未上锁的锁上调用,抛runtimeerror异常。

上例的锁的实现

importthreadingfrom threading importThread,Lockimportloggingimporttime

FORMAT= "%(asctime)s %(threadName)s %(thread)d %(message)s"logging.basicConfig(format= FORMAT,level =logging.INFO)

cups=[]

lock=Lock()def worker(count = 10):

logging.info("i'm working for u")

flag=FalsewhileTrue:

lock.acquire()#获取锁

if len(cups)>=count:

flag=True#lock.release()#1 这里释放锁?

time.sleep(0.0001)#为了看出线程切换效果

if notflag:

cups.append(1)#lock.release()#2 这里释放锁?

ifflag:breaklogging.info("i finished . cups = {}".format(len(cups)))for _ in range(10):

Thread(target= worker,args = (1000,)).start()

思考,上面的代码中,共有两处可以释放锁,放在何处合适?

假设位置1的lock.release()合适,分析如下:

有一个时刻,在某一个线程中len(cups)正好是999,flag= true,释放锁,正好线程被打断。另一个线程判断发现也是999,flag= true,可能线程被打断。可能另外一个线程也判断是999,flag也设置为true。这3个线程只要继续执行到cups.append(1),一定会导致cups的长度超过1000.

假设位置2的lock.release()合适,分析如下:

在某一个时刻,len(cups)正好是999,flag= true,其他线程试图访问这段代码的线程都阻塞获取不到锁,直到当前线程安全的增加了一个数据,然后释放锁,其他线程有一个抢到锁,但发现已经1000了,只好break打印退出。再其他线程 都一样,发现已经1000咯。都退出了。

所以位置2释放锁是正确的。

但是我们发现锁保证了数据完整性,但是性能下降很多。

上例中if flag:break是为了保证release方法被执行,否则,就出现了死锁,得到的锁永远没有释放锁。

计数器类,可以加,可以减。

importthreadingfrom threading importThread,LockimporttimeclassCounter:def __init__(self):

self._val=0

@propertydefvalue(self):returnself._valdefinc(self):

self._val+=1

defdec(self):

self._val-=1

def run(c:Counter,count = 100):#重复了100次,50次加,50次减,最后结果应该0.for _ inrange(count):for i in range(-50,50):if i<0:

c.dec()else:

c.inc()

c=Counter()

c1= 10 #线程数

c2 = 1000

for i inrange(c1):

Thread(target=run,args =(c,c2)).start()print(c.value)#这一句有问题,可能线程还没有启动,这一句就完了。可以让主线程睡一会。

c1 取10、100、1000看看

c2取10、100、1000看看。当是1000的时候,结果就不对了。而且应该注意,最后一句打印的结果不一定是最终结果,可能线程还在运行。

self._val+=1或self._val-=1在线程执行的时候,有可能被打断。

要加锁,怎么加?

加锁,解锁

一般来说,加锁就需要解锁,但是加锁后,解锁前,还要有一些代码执行,就有可能会抛异常,一旦出现异常,锁是无法释放,但是当前线程可能因为这个异常被终止了,这就产生了死锁。

加锁,解锁常用语句:

使用try……finally语句保证锁的释放。

with上下文管理,锁对象支持上下文管理。

改造counter类,如下:

importthreadingfrom threading importThread,LockimporttimeclassCounter:def __init__(self):

self._val=0

self.__lock =Lock()

@propertydefvalue(self):

with self.__lock:returnself._valdefinc(self):try:

self.__lock.acquire()

self._val+=1

finally:

self.__lock.release()defdec(self):

with self.__lock:

self._val-= 1

def run(c:Counter,count = 100):for _ inrange(count):for i in range(-50,50):if i<0:

c.dec()else:

c.inc()

c=Counter()

c1= 10 #线程数

c2 = 1000

for i inrange(c1):

Thread(target=run,args =(c,c2)).start()print(c.value)#这一句合适吗?

最后一句修改如下:

importthreadingfrom threading importThread,LockimporttimeclassCounter:def __init__(self):

self._val=0

self.__lock =Lock()

@propertydefvalue(self):

with self.__lock:#__enter__和__exit__returnself._valdefinc(self):try:

self.__lock.acquire()

self._val+=1

finally:

self.__lock.release()defdec(self):

with self.__lock:

self._val-= 1

def run(c:Counter,count = 100):for _ inrange(count):for i in range(-50,50):if i<0:

c.dec()else:

c.inc()

c=Counter()

c1= 10 #线程数

c2 = 1000

for i inrange(c1):

Thread(target=run,args =(c,c2)).start()whileTrue:

time.sleep(1)if threading.active_count() ==1:print(threading.enumerate())print(c.value)break

else:print(threading.enumerate())

print(v.value)这一句在主线程中,很早就执行完了。退出条件是,只剩下主线程的时候。

锁的应用场景

锁使用于访问和修改同一个共享资源的时候,即读写同一个资源的时候。

如果全部都是读取同一个共享资源需要锁吗?

不需要,因为这时可以认为共享资源是不可变的,每一次读取它都是一样的值,所以不用加锁。

使用锁的注意事项:

少用锁,必要时用锁,使用了锁,多线程访问被锁的资源时,就成了串行,要么排队执行,要么争抢执行。

举例,高速公路上车并行跑,可是到了省界只开放了一个收费口,过了这个口,车辆依然可以在多车道上一起跑。过收费口的时候,如果排队一辆辆过,加不加锁一样效率相当,但是一旦出现争抢,就必须加锁一辆辆过。

加锁时间越短越好,不需要就立即释放锁。

一定要避免死锁。

不使用锁,有了效率,但是结果是错的。

使用了锁,效率低下,但是结果是正确的。

所以,我们是为了效率要错误的结果呢?还是为了正确的结果,让计算机去计算?

非阻塞锁使用

importthreadingimporttimeimportlogging

FORMAT= "%(asctime)-15s\t [%(threadName)s,%(thread)8d] %(message)s"logging.basicConfig(level=logging.INFO,format =FORMAT)defworker(tasks):for task intasks:

time.sleep(0.001)if task.lock.acquire(False):#获取锁则返回true

logging.info("{} {} begin to start".format(threading.current_thread(),task.name))#适当的时机释放锁

else:

logging.info("{} {} is working".format(threading.current_thread(),task.name))classTask:def __init__(self,name):

self.name=name

self.lock=threading.Lock()#构造10个任务

tasks = [Task("task - {}".format(x)) for x in range(10)]#启动5个线程

for i in range(5):

threading.Thread(target=worker,name="worker - {}".format(i),args=(tasks,)).start()

可重入锁RLock

可重入锁,是线程相关的锁。

线程A获得可重复锁,并可以多次成功获取,不会阻塞,最后要在线程A中做和asquire次数相同的release.

importthreading

lock=threading.RLock()

ret=lock.acquire()print(ret)

ret=lock.acquire()print(ret)

结果为:

True

True

没有释放锁,后面还是拿到了锁。

importthreadingimporttime

lock=threading.RLock()print(lock.acquire())print("________________________")print(lock.acquire(blocking=False))print(lock.acquire())print(lock.acquire(timeout = 3.55))print(lock.acquire(blocking=False))#print(lock.acquire(blocking=False,timeout=10))#异常

lock.release()

lock.release()

lock.release()

lock.release()print("main thread {}".format(threading.current_thread().ident))print("locked in main thread {}".format(lock))#注意观察lock对象的信息

lock.release()#lock.release()#多了一次

print("========================")print()print(lock.acquire(blocking=False))#1次#threading.Timer(3,lambda x:x.release(),args=(lock,)).start()#跨线程了,异常

lock.release()print("```````````````````````````````````")print()#测试多线程

print(lock.acquire())defsub(l):print("{}:{}".format(threading.current_thread(),l.acquire()))#阻塞

print("{}:{}".format(threading.current_thread(), l.acquire(False)))print("lock in sub thread {}".format(lock))

l.release()print("sub 1")

l.release()print("sub 2")#l.release()#多了一次

threading.Timer(2,sub, args=(lock,)).start()#传入同一个lock对象

print("+++++++++++++++++++++++++++++")print()print(lock.acquire())

lock.release()

time.sleep(5)print("释放主线程锁")

lock.release()

结果为:

True

________________________

True

True

True

True

main thread 12652

locked in main thread

========================

True

```````````````````````````````````

True

+++++++++++++++++++++++++++++

True

释放主线程锁

:True

:True

lock in sub thread

sub 1

sub 2

可重入锁,与线程相关,可在一个线程中获取锁,并可继续在同一个线程中不阻塞获取锁。当锁未释放完,其他线程获取锁就会阻塞,直到当前持有锁的线程释放完锁。

Condition

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

acquire(*args):获取锁

wait(self,timeout = None):等待或超时

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

notify_all():唤醒所有等待的线程

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

先看一个例子,消费者消费速度大于生产者生产速度。

from threading importThread,Eventimportloggingimportrandom

FORMAT= "%(asctime)s %(threadName)s (thread)d %(message)s"logging.basicConfig(format= FORMAT,level=logging.INFO)#此例只是为了演示,不考虑线程安全问题。

classDispatcher():def __init__(self):

self.data=None

self.event= Event()#event只是为了使用方便,与逻辑无关

defproduce(self,total):for _ inrange(total):

data= random.randint(0,100)

logging.info(data)

self.data=data

self.event.wait(1)

self.event.set()defconsum(self):while notself.event.is_set():

data=self.data

logging.info("recieved {}".format(data))

self.data=None

self.event.wait(0.5)

d=Dispatcher()

p= Thread(target=d.produce,args=(10,),name="producer")

c= Thread(target=d.consum,name="consumer")

c.start()

p.start()

结果为:

2019-11-26 16:39:02,354 consumer (thread)d recieved None

2019-11-26 16:39:02,355 producer (thread)d 100

2019-11-26 16:39:02,855 consumer (thread)d recieved 100

2019-11-26 16:39:03,369 producer (thread)d 37

2019-11-26 16:39:03,369 consumer (thread)d recieved 37

2019-11-26 16:39:03,869 consumer (thread)d recieved None

2019-11-26 16:39:04,383 producer (thread)d 20

2019-11-26 16:39:04,383 consumer (thread)d recieved None

2019-11-26 16:39:04,884 consumer (thread)d recieved None

2019-11-26 16:39:05,397 producer (thread)d 65

2019-11-26 16:39:05,397 consumer (thread)d recieved 65

2019-11-26 16:39:05,897 consumer (thread)d recieved None

2019-11-26 16:39:06,411 consumer (thread)d recieved None

2019-11-26 16:39:06,412 producer (thread)d 9

2019-11-26 16:39:06,912 consumer (thread)d recieved 9

2019-11-26 16:39:07,425 producer (thread)d 66

2019-11-26 16:39:07,425 consumer (thread)d recieved 66

2019-11-26 16:39:07,925 consumer (thread)d recieved None

2019-11-26 16:39:08,439 consumer (thread)d recieved None

2019-11-26 16:39:08,439 producer (thread)d 67

2019-11-26 16:39:08,939 consumer (thread)d recieved 67

2019-11-26 16:39:09,453 consumer (thread)d recieved None

2019-11-26 16:39:09,453 producer (thread)d 58

2019-11-26 16:39:09,953 consumer (thread)d recieved 58

2019-11-26 16:39:10,467 producer (thread)d 24

2019-11-26 16:39:10,467 consumer (thread)d recieved 24

2019-11-26 16:39:10,967 consumer (thread)d recieved None

2019-11-26 16:39:11,481 consumer (thread)d recieved None

2019-11-26 16:39:11,481 producer (thread)d 61

2019-11-26 16:39:11,981 consumer (thread)d recieved 61

这个例子采用了消费者主动消费,消费者浪费了大量时间,主动来查看有没有数据。

能否换成一种通知机制,有数据通知消费者来消费呢?

使用Condition对象

from threading importThread, Event, Conditionimportloggingimportrandom

FORMAT= "%(asctime)s %(threadName)s (thread)d %(message)s"logging.basicConfig(format=FORMAT, level=logging.INFO)#此例只是为了演示,不考虑线程安全问题。

classDispatcher():def __init__(self):

self.data=None

self.event= Event() #event只是为了使用方便,与逻辑无关

self.cond =Condition()defproduce(self, total):for _ inrange(total):

data= random.randint(0, 100)

with self.cond:

logging.info(data)

self.data=data

self.cond.notify_all()

self.event.wait(1)#模拟产生数据速度

self.event.set()defconsum(self):while notself.event.is_set():

with self.cond:

self.cond.wait()#阻塞等通知

logging.info("recieved {}".format(self.data))

self.data=None

self.event.wait(0.5)

d=Dispatcher()

p= Thread(target=d.produce, args=(10,), name="producer")

c= Thread(target=d.consum, name="consumer")

c.start()

p.start()

结果为:

2019-11-26 16:40:26,151 producer (thread)d 23

2019-11-26 16:40:26,151 consumer (thread)d recieved 23

2019-11-26 16:40:27,158 producer (thread)d 71

2019-11-26 16:40:27,158 consumer (thread)d recieved 71

2019-11-26 16:40:28,172 producer (thread)d 35

2019-11-26 16:40:28,172 consumer (thread)d recieved 35

2019-11-26 16:40:29,186 producer (thread)d 15

2019-11-26 16:40:29,186 consumer (thread)d recieved 15

2019-11-26 16:40:30,200 producer (thread)d 26

2019-11-26 16:40:30,201 consumer (thread)d recieved 26

2019-11-26 16:40:31,215 producer (thread)d 42

2019-11-26 16:40:31,215 consumer (thread)d recieved 42

2019-11-26 16:40:32,229 producer (thread)d 63

2019-11-26 16:40:32,229 consumer (thread)d recieved 63

2019-11-26 16:40:33,243 producer (thread)d 50

2019-11-26 16:40:33,243 consumer (thread)d recieved 50

2019-11-26 16:40:34,257 producer (thread)d 50

2019-11-26 16:40:34,257 consumer (thread)d recieved 50

2019-11-26 16:40:35,271 producer (thread)d 50

2019-11-26 16:40:35,271 consumer (thread)d recieved 50

上例中,消费者等待数据等待,如果生产者准备好了会通知消费者消费,省得消费者反复来查看数据是否就绪。

如果是1个生产者,多个消费者怎么改?

from threading importThread, Event, Conditionimportloggingimportrandom

FORMAT= "%(asctime)s %(threadName)s (thread)d %(message)s"logging.basicConfig(format=FORMAT, level=logging.INFO)#此例只是为了演示,不考虑线程安全问题。

classDispatcher():def __init__(self):

self.data=None

self.event= Event() #event只是为了使用方便,与逻辑无关

self.cond =Condition()defproduce(self, total):for _ inrange(total):

data= random.randint(0, 100)

with self.cond:

logging.info(data)

self.data=data

self.cond.notify_all()

self.event.wait(1)#模拟产生数据速度

self.event.set()defconsum(self):while notself.event.is_set():

with self.cond:

self.cond.wait()#阻塞等通知

logging.info("recieved {}".format(self.data))

self.event.wait(0.5)#模拟消费速度

d=Dispatcher()

p= Thread(target=d.produce, args=(10,), name="producer")for i in range(5):

c= Thread(target=d.consum,name="consumer-{}".format(i))

c.start()

p.start()

2019-11-26 16:41:38,536 producer (thread)d 26

2019-11-26 16:41:38,536 consumer-4 (thread)d recieved 26

2019-11-26 16:41:38,536 consumer-3 (thread)d recieved 26

2019-11-26 16:41:38,536 consumer-0 (thread)d recieved 26

2019-11-26 16:41:38,537 consumer-2 (thread)d recieved 26

2019-11-26 16:41:38,537 consumer-1 (thread)d recieved 26

2019-11-26 16:41:39,543 producer (thread)d 80

2019-11-26 16:41:39,543 consumer-1 (thread)d recieved 80

2019-11-26 16:41:39,544 consumer-4 (thread)d recieved 80

2019-11-26 16:41:39,544 consumer-2 (thread)d recieved 80

2019-11-26 16:41:39,544 consumer-3 (thread)d recieved 80

2019-11-26 16:41:39,544 consumer-0 (thread)d recieved 80

2019-11-26 16:41:40,557 producer (thread)d 17

2019-11-26 16:41:40,557 consumer-4 (thread)d recieved 17

2019-11-26 16:41:40,557 consumer-2 (thread)d recieved 17

2019-11-26 16:41:40,557 consumer-0 (thread)d recieved 17

2019-11-26 16:41:40,558 consumer-1 (thread)d recieved 17

2019-11-26 16:41:40,558 consumer-3 (thread)d recieved 17

2019-11-26 16:41:41,571 producer (thread)d 73

2019-11-26 16:41:41,571 consumer-1 (thread)d recieved 73

2019-11-26 16:41:41,572 consumer-3 (thread)d recieved 73

2019-11-26 16:41:41,572 consumer-0 (thread)d recieved 73

2019-11-26 16:41:41,573 consumer-2 (thread)d recieved 73

2019-11-26 16:41:41,573 consumer-4 (thread)d recieved 73

2019-11-26 16:41:42,585 producer (thread)d 4

2019-11-26 16:41:42,585 consumer-3 (thread)d recieved 4

2019-11-26 16:41:42,585 consumer-2 (thread)d recieved 4

2019-11-26 16:41:42,586 consumer-0 (thread)d recieved 4

2019-11-26 16:41:42,586 consumer-4 (thread)d recieved 4

2019-11-26 16:41:42,586 consumer-1 (thread)d recieved 4

2019-11-26 16:41:43,599 producer (thread)d 100

2019-11-26 16:41:43,599 consumer-0 (thread)d recieved 100

2019-11-26 16:41:43,599 consumer-4 (thread)d recieved 100

2019-11-26 16:41:43,599 consumer-2 (thread)d recieved 100

2019-11-26 16:41:43,600 consumer-1 (thread)d recieved 100

2019-11-26 16:41:43,600 consumer-3 (thread)d recieved 100

2019-11-26 16:41:44,613 producer (thread)d 68

2019-11-26 16:41:44,614 consumer-1 (thread)d recieved 68

2019-11-26 16:41:44,614 consumer-2 (thread)d recieved 68

2019-11-26 16:41:44,615 consumer-0 (thread)d recieved 68

2019-11-26 16:41:44,615 consumer-4 (thread)d recieved 68

2019-11-26 16:41:44,616 consumer-3 (thread)d recieved 68

2019-11-26 16:41:45,626 producer (thread)d 8

2019-11-26 16:41:45,626 consumer-0 (thread)d recieved 8

2019-11-26 16:41:45,626 consumer-1 (thread)d recieved 8

2019-11-26 16:41:45,626 consumer-2 (thread)d recieved 8

2019-11-26 16:41:45,627 consumer-3 (thread)d recieved 8

2019-11-26 16:41:45,627 consumer-4 (thread)d recieved 8

2019-11-26 16:41:46,640 producer (thread)d 15

2019-11-26 16:41:46,640 consumer-0 (thread)d recieved 15

2019-11-26 16:41:46,641 consumer-4 (thread)d recieved 15

2019-11-26 16:41:46,641 consumer-3 (thread)d recieved 15

2019-11-26 16:41:46,641 consumer-2 (thread)d recieved 15

2019-11-26 16:41:46,641 consumer-1 (thread)d recieved 15

2019-11-26 16:41:47,661 producer (thread)d 48

2019-11-26 16:41:47,662 consumer-2 (thread)d recieved 48

2019-11-26 16:41:47,662 consumer-1 (thread)d recieved 48

2019-11-26 16:41:47,663 consumer-3 (thread)d recieved 48

2019-11-26 16:41:47,663 consumer-4 (thread)d recieved 48

2019-11-26 16:41:47,663 consumer-0 (thread)d recieved 48

self.cond.notify_all()#发通知

修改为self.cond.notify(2)

试一试看看效果?

这个例子,可以看到实现了消息的一对多,这其实就是广播模式。

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

Condition总结

Condition用于生产者消费者模型中,解决生产者消费者速度匹配的问题。

采用了通知机制,非常有效率。

使用方式

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

消费者wait,等待通知。

生产者生产好消息,对消费者发通知,可以使用notify或者notify_all方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值