python编程入门后学习笔记六——信号量、Event、队列、生产者消费者模型

本节内容

1.信号量 threading.BoundedSemaphore()

2.threading.Event()实现线程间通信(简易红绿灯的实现)

3.队列queue

4.生成者消费者模型

 

1.信号量 threading.BoundedSemaphore()                                                                                                              

互斥锁(threading.Lock()同一时刻只允许一个线程更改数据;

信号量(threading.BoundedSemaphore())是允许同一时刻一定数量的线程更改数据,比如一个房间有5个电话机,那最多只允许5个人打电话,后面的人只能等里面的人出来后才能再进去。

#-*- coding:utf-8 -*-
#Author:'Yang'
import threading
import time
def run(n):
    semaphore.acquire()
    print("run the thread:",n)
    time.sleep(2)
    semaphore.release()

if __name__=="__main__":
    semaphore=threading.BoundedSemaphore(5) #声明信号量,有5把锁,最多允许5个线程同时进行
    for i in range(22):
        t=threading.Thread(target=run,args=(i,))
        t.start()

while threading.active_count()!=1:
    pass
else:
    print("all threads finished".center(50,"*"))
threading_semaphore.py

看上去是5个5个同时完成,其实是每出来一个就进去一个。

注意这种情况,如果5个同时改数据就可能改错了,这种情况一般应用于连接池、线程池等等,如MySQL.......同一时刻允许放行多少个连接,就可以使用信号量。

 

2.threading.Event()实现线程间通信(简易红绿灯的实现)                                                                                       

threading.Event() 用来实现两个或多个线程的交互。

红绿灯的实现:

启动一个交通灯线程

交通灯规则是初始设置为绿灯-->绿灯闪烁20秒-->跳为红灯-->红灯闪烁10秒-->计数清零-->又从绿灯开始,如此反复循环。

同时生成若干个车辆线程

车辆行驶规则是红灯停,绿灯行。

#-*- coding:utf-8 -*-
#Author:'Yang'

import time
import threading

event=threading.Event()
'''红绿灯:绿灯开始,20秒后变红灯,红灯10秒,重置标志位(计数清零),红灯再变绿灯,如此类推...'''
def lighter():
    count=0
    event.set() #设置标志位,为绿灯
    while True:
        if count>20 and count<=30: #红灯
            event.clear() #把标志位清空
            print("\033[41;1mred light is on...\033[0m")
        elif count>30:
            event.set() #变绿等
            count=0
        else:
            print("\033[42;1mgreen light is on...\033[0m")

        time.sleep(1)
        count +=1

def vehicle(v_type):
    while True:
        if event.is_set():#检查标志位(绿灯)
            print("[%s] is running..." %v_type)
            time.sleep(1)
        else:
            print("\033[33;1m[%s] sees red light,waiting...\033[0m" %v_type)
            event.wait()    #卡住等待,直到标志位被设定
            print("\033[34;1m[%s] green light is on,start going...\033[0m" %v_type)


light=threading.Thread(target=lighter,)
light.start()

vehicle1=threading.Thread(target=vehicle,args=("别克君威",))
vehicle1.start()
vehicle2=threading.Thread(target=vehicle,args=("别克威朗",))
vehicle2.start()

 #event.wait()  等待标志位被设定

#event.clear()    把标志位清空

#event.set()    设定标志位

#event.is_set()  判断标志位是否被设定

3.队列queue                                                                                                                                                              

队列是提供了一个适用于多线程编程先进先出的数据结构, queue是Python标准库中的线程安全的队列(FIFO)实现,用来在生产者和消费者线程之间的信息传递。

常用的三种队列

1)class queue.Queue()  #先入先出的队列

那么怎么放数据,怎么取数据呢?

>>> import queue
>>> q=queue.Queue()
>>> q.put(1) #放数据
>>> q.put(2) #放数据
>>> q.put(3) #放数据
>>> q.qsize() #查看队列的大小
3
>>> q.get() #取数据,先进的先取到
1
>>> q.get() #取数据
2
>>> q.get() #取数据
3
>>> q.get_nowait() #数据取完了,会报queue.Empty
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    q.get_nowait()
queue.Empty

注意:

当使用队列里数据已经取完,再调用

q.get_nowait()

q.get(block=False)

都会报 queue.Empty

如果数据取完后,又执行了q.get() 会出现卡死的状态,这时建议做个判断 if q.qsize()==0  就不要取了。

用while对上述代码进行改进:

>>> import queue
>>> q=queue.Queue()
>>> q.put(1)
>>> q.put(2)
>>> q.put(3)
>>> while q.qsize!=0:
      q.get()

    
1
2
3

如果我们想设置队列的大小,直接设置maxsize的值,例如

>>> q=queue.Queue(maxsize=3)    #设置队列大小为3
>>> q.put(1)   #放第一个数据
>>> q.put(2)   #放第二个数据
>>> q.put(3)   #放第三个数据
>>> q.put(4)   #放第四个数据,卡死,放不进去

2)class queue.LifoQueue()  #后进先出的队列

>>> import queue
>>> q=queue.LifoQueue()
>>> q.put(1)
>>> q.put(2)
>>> q.put(3)
>>> while q.qsize!=0:
      q.get()

    
3
2
1

3)class queue.PriorityQueue()   #存储数据时可设置优先级的队列

#-*- coding:utf-8 -*-
import queue

#存储数据时可设置优先级的队列,跟数据的存放先后顺序无关
q=queue.PriorityQueue() 

q.put((2,"Tom"))
q.put((1,"Jimmy"))
q.put((3,"Lucy"))

while q.qsize()!=0:
    print(q.get()) #取数据

执行结果:

(1, 'Jimmy')
(2, 'Tom')
(3, 'Lucy')

分析:输出结果是根据优先级 1 2 3 进行排序的。

4.生产者消费者模型                                                                                                                                                     

Q1:生产者和消费者模型的来由?

A:

在线程的世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。

多线程的开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等消费者处理完,才能继续生产数据;相反,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题,于是,引入了生产者和消费者模型。

生产者和消费者模型通过平衡生产线程和消费线程的处理能力提高程序的整体处理数据的速度,能够解决绝大多数并发问题。

Q2:什么是生产者和消费者模型? 

A:

生产者和消费者模型是通过一个容器来解决生产者和消费者的强耦合问题

生产者和消费者彼此之间不直接通讯,而是通过阻塞队列来进行通讯。所以,生产者生产完数据之后不用等消费者处理,直接扔给阻塞队列消费者也不找生产者直接要数据,而是直接从阻塞队列中取(数据),阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

看个生产馒头吃馒头的例子,你就能明白什么是生产者和消费者模型,以及什么情况下使用这个模型了:

#-*- coding:utf-8 -*-
#Author:'Yang'

import threading
import queue
import time

q=queue.Queue() #阻塞队列,用于生产者存放做好的馒头,消费者取走存放的馒头
'''生产者,生产一个馒头耗时约为0.6秒'''
def Producer(name):
    count=0
    while True:
        count +=1
        q.put("馒头%s" %count)
        print("[%s]生产了[馒头%s]" %(name,count))
        time.sleep(0.6)

'''消费者,取到并吃掉一个馒头耗时约为1秒'''
def Consumer(name):
    while True:
        print("[%s] 取到了[%s],然后吃了它..." %(name,q.get()))
        time.sleep(1)


p1=threading.Thread(target=Producer,args=("Lily",))     #生产者1
p2=threading.Thread(target=Producer,args=("David",))    #生产者2
c1=threading.Thread(target=Consumer,args=("Jimmy",))    #消费者1
c2=threading.Thread(target=Consumer,args=("Poly",))     #消费者2
c3=threading.Thread(target=Consumer,args=("Jone",))     #消费者3

p1.start()
p2.start()
c1.start()
c2.start()
c3.start()
View Code

这段代码的执行结果是:

[Lily]生产了[馒头1]
[David]生产了[馒头1]
[Jimmy] 取到了[馒头1],然后吃了它...
[Poly] 取到了[馒头1],然后吃了它...
[Jone] 取到了[馒头2],然后吃了它...
[Lily]生产了[馒头2]
[David]生产了[馒头2]
[Jimmy] 取到了[馒头2],然后吃了它...
[Lily]生产了[馒头3]
[David]生产了[馒头3]
[Poly] 取到了[馒头3],然后吃了它...
[Jone] 取到了[馒头3],然后吃了它...

 

 

这是一个典型的多并发的协作.......直到人为终止运行,该程序结束。

 

posted on 2017-07-25 09:19  奔跑的蜗牛~~  阅读(180)  评论(0)    收藏  举报

导航