一.信号量
信号量和锁相似,锁同一时间只允许一个对象(进程)通过,信号量同一时间允许多个对象(进程)通过。
1.semaphore信号量原理
多线程同时运行,能提高程序的运行效率,但是并非线程越多越好,而semaphore信号量可以通过内置计数器来控制同时运行线程的数量,启动线程(消耗信号量)内置计数器会自动减一,线程结束(释放信号量)内置计数器会自动加一;内置计数器为零,启动线程会阻塞,直到有本线程结束或者其他线程结束为止;
2.semaphore信号量相关函数介绍
acquire() — 消耗信号量,内置计数器减一;
release() — 释放信号量,内置计数器加一;
在semaphore信号量有一个内置计数器,控制线程的数量,acquire()会消耗信号量,计数器会自动减一;release()会释放信号量,计数器会自动加一;当计数器为零时,acquire()调用被阻塞,直到release()释放信号量为止。
例子:
import threading
import time
def run(n,x):
semaphore.acquire()
print(n)
time.sleep(x)
semaphore.release()
if __name__ == '__main__':
semaphore = threading.Semaphore()
for i in range(50):
t=threading.Thread(target=run, args=(i,))
t.start()
二.条件变量
Condition(条件变量)通常与一个锁关联。需要在多个Contidion中共享一个锁时,可以传递一个Lock/RLock实例给构造方法,否则它将自己生成一个RLock实例。
Condition()包括以下几点:
1.acquire(): 线程锁
2.release(): 释放锁
3.wait(timeout): 线程挂起,直到收到一个notify通知或者超时才会被唤醒继续运行。wait()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。
4.notify(n=1): 通知其他线程,那些挂起的线程接到这个通知之后会开始运行,默认是通知一个正等待该condition的线程,最多则唤醒n个等待的线程。notify()必须在已获得Lock前提下才能调用,否则会触发RuntimeError。notify()不会主动释放Lock。
notifyAll(): 如果wait状态线程比较多,notifyAll的作用就是通知所有线程
例如:
import threading
import time
def run(x):
#lock.acquire()
con.acquire()
print(f'线程{x}')
con.notify()
print(f'线程{x}挂起')
con.wait()
time.sleep(1)
print(f'线程{x}再次启动')
con.notify()
con.release()
#lock.release()
def run2(x):
#lock.acquire()
con.acquire()
print(f'线程{x}')
con.notify()
print(f'线程{x}挂起')
con.wait()
time.sleep(1)
print(f'线程{x}再次启动')
con.notify()
con.release()
#lock.release()
if __name__ == '__main__':
con = threading.Condition()
#for i in range(50):
#lock=threading.Lock()
#t=threading.Thread(target=run, args=(i,))
#t.start()
t1 = threading.Thread(target=run, args=(1,))
t1.start()
t2 = threading.Thread(target=run, args=(2,))
t2.start()
事件
1.python事件Event原理
事件event中有一个全局内置标志Flag,值为 True 或者False。使用wait()函数的线程会处于阻塞状态,此时Flag指为False,直到有其他线程调用set()函数让全局标志Flag置为True,其阻塞的线程立刻恢复运行,还可以用isSet()函数检查当前的Flag状态.
2.python事件Event相关函数介绍
set() — 全局内置标志Flag,将标志Flag 设置为 True,通知在等待状态(wait)的线程恢复运行;
isSet() — 获取标志Flag当前状态,返回True 或者 False;
wait() — 一旦调用,线程将会处于阻塞状态,直到等待其他线程调用set()函数恢复运行;
clear() — 将标志设置为False;
例如:
import threading
import time
def car():
while True:
if event.is_set():
print("小车行驶")
else:
print("小车停止")
event.wait()
def set_event():
while True:
event.set()
time.sleep(1)
event.clear()
time.sleep(1)
if __name__ == '__main__':
event = threading.Event()
car1 = threading.Thread(target=car)
car1.start()
set_e = threading.Thread(target=set_event)
set_e.start()
而对于本次的作业:使用类继承的方式,实现信号量、事件功能操作。具体案例:第一个
线程中获取当前时间,判断当前时间3秒之后,触发“事件” 对象。在另
一个线程中,作为数学考试结束的判断变量,否则一直处于考试中,并打
印。
import threading # 导入threading模块
import time # 导入time模块
import datetime # 导入datetime模块
class Time(threading.Thread): # 创建多线程的第一个类
def __init__(self): # 初始化方法
super(Time, self).__init__() # 继承父类方法
def run(self): # 重写父类方法
print(datetime.datetime.now()) # 输出当前的年月日时分秒
event.set() # 设置事件
class MathExam(threading.Thread): # 创建类
def __init__(self): # 初始化方法
super(MathExam, self).__init__() # 继承父类方法
def run(self): # 重写父类方法
while True:
time.sleep(3) # 时间延迟3秒
if event.is_set(): # 如果事件设定的话
print("数学考试结束") # 打印数学考试结束
break # 结束循环
else: # 否则的话
print("考试中") # 打印考试
if __name__ == "__main__":
event = threading.Event() # 创建事件
t1 = Time() # 实例化对象
t1.start() # 开始执行线程
t2 = MathExam() # 实例化对象
t2.start() # 开始执行线程