信号量:
信号量是由操作系统管理的一种抽象数据类型,用于在多线程中同步对共享资源的使用。本质上说,信号量是一个内部数据,用于标明当前的共享资源可以有多少并发读取。
threading模块里的Semaphore类实现了信号量对象,可用于控制获取资源的线程数量。所具有的acquire()和release()方法,可以用with语句的上下文管理器。当进入时,将调用acquire()方法,当退出时,将调用release()。
作用及应用:
主要用在数据库应用中,比如连接数据库的连接,限制同时连接的数量,如数据库连接池。
如上图所示,定义一个run方法,其中参数n表示输出的线程,用x来控制输出线程的延长时间,其中semaphore=threading,Seamphore(5),数字五来控制每次输出的线程的个数不能超过5
条件变量:
Python 提供的 Condition 对象提供了对复杂线程同步问题的支持。 Condition 被称为条件变量,除了提供与 Lock 类似的 acquire 和 release 方法外,还提供了 wait 和 notify 方法。
构造方法:
import threading
# 可传入一个互斥锁或者可重入锁
cond = threading.Condition()
实例方法:
acquire([timeout])/release(): 调用关联的锁的相应方法。
wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。
使用前线程必须已获得锁定,否则将抛出异常。
notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用
acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会
释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池
尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常
从网上找的一个案例帮助理解:
生产者与消费者模式,以吃火锅为例:一盘老肉片有10块肉,吃完了又重新往锅里加….
生产者:往锅里加老肉片,每次加一盘(10块);
消费者:吃煮熟的肉片,mei吃一片,肉片数量减一,吃完为止;
# 导入线程模块
import threading
import time
# 创建条件变量condition
con = threading.Condition()
meat_num = 0
def thread_consumers():
# 条件变量condition 线程上锁
con.acquire()
# 全局变量声明关键字 global
global meat_num
meat_num = 0
# 等待肉片下锅煮熟
con.wait()
while True:
print("我来一块肉片...")
meat_num -= 1
print("剩余肉片数量:%d"%meat_num)
time.sleep(0.5)
if meat_num == 0:
# 肉片吃光了,通知老板添加肉片
print("老板,再来一份老肉片...")
con.notify()
# 肉片吃光了,等待肉片
con.wait()
# 条件变量condition 线程释放锁
con.release()
def thread_producer():
# 条件变量condition 线程上锁
con.acquire()
# 全局变量声明关键字 global
global meat_num
# 肉片熟了,可以开始吃了
meat_num = 10
print("肉片熟了,可以开始吃了...")
con.notify()
while True:
# 阻塞函数,等待肉片吃完的通知
con.wait()
meat_num = 10
# 添加肉片完成,可以继续开吃
print("添加肉片成功!当前肉片数量:%d"%meat_num)
time.sleep(1)
con.notify()
con.release()
if __name__ == "__main__":
# 创建并初始化线程
t1 = threading.Thread(target=thread_producer)
t2 = threading.Thread(target=thread_consumers)
# 启动线程 -- 注意线程启动顺序,启动顺序很重要
t2.start()
t1.start()
# 阻塞主线程,等待子线程结束
t1.join()
t2.join()
print("程序结束!")
事件:
Event(事件):事件处理的机制:全局定义了一个内置标志Flag,如果Flag值为 False,那么当程序执行 event.wait方法时就会阻塞,如果Flag值为True,那么event.wait 方法时便不再阻塞。
Event其实就是一个简化版的 Condition。Event没有锁,无法使线程进入同步阻塞状态。
Event()
-
set(): 将标志设为True,并通知所有处于等待阻塞状态的线程恢复运行状态。
-
clear(): 将标志设为False。
-
wait(timeout): 如果标志为True将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()。
-
isSet(): 获取内置标志状态,返回True或False。
案例:
小伙伴a和b准备就绪,当收到通知event.set()的时候,会执行a和b线程
# coding:utf-8
import threading
import time
event = threading.Event()
def chihuoguo(name):
# 等待事件,进入等待阻塞状态
print '%s 已经启动' % threading.currentThread().getName()
print '小伙伴 %s 已经进入就餐状态!'%name
time.sleep(1)
event.wait()
# 收到事件后进入运行状态
print '%s 收到通知了.' % threading.currentThread().getName()
print '小伙伴 %s 开始吃咯!'%name
# 设置线程组
threads = []
# 创建新线程
thread1 = threading.Thread(target=chihuoguo, args=("a", ))
thread2 = threading.Thread(target=chihuoguo, args=("b", ))
# 添加到线程组
threads.append(thread1)
threads.append(thread2)
# 开启线程
for thread in threads:
thread.start()
time.sleep(0.1)
# 发送事件通知
print '主线程通知小伙伴开吃咯!'
event.set()
运行结果:
Thread-1 已经启动
小伙伴 a 已经进入就餐状态!
Thread-2 已经启动
小伙伴 b 已经进入就餐状态!
主线程通知小伙伴开吃咯!
Thread-1 收到通知了.
小伙伴 a 开始吃咯!
Thread-2 收到通知了.
小伙伴 b 开始吃咯!