学习视频链接: python语言爱好者的个人空间_哔哩哔哩_Bilibili P70、P71、P72
信号量使用示例代码以及相关作用
信号量:是最古老的同步原语之一,是一个计数器。当资源释放时计数器就会递增,当资源申请时计数器就会递减。可以认为信号量就代表着资源是否可用
使用方法:
"""信号量"""
import threading
import time
def run(n,x):
semaphore.acquire() # 对该线程上锁
print(n)
time.sleep(x) # 延迟1秒
semaphore.release() # 释放锁资源
if __name__ == '__main__':
semaphore = threading.Semaphore(3) # 将信号量实例化 括号里填写同时执行线程的个数
for i in range(50):
t = threading.Thread(target=run, args=(i, i)) # 调用threading.Thread()函数 target=方法名 args(参数, ) 如果没有参数,则threading.Thread(target=方法名)即可
#args=(i, i) 中的参数对应 “target=方法名” 中方法的参数
t.start() # 线程执行语句
from threading import BoundedSemaphore
MAX = 3 # 资源最大值
semaphore = BoundedSemaphore(MAX)
print(semaphore._value)
semaphore.acquire()
print(semaphore._value)
semaphore.acquire()
print(semaphore._value)
semaphore.acquire()
print(semaphore._value) # 0
semaphore.acquire(False) # 没有资源可以申请,value的值为0,再次调用acquire方法会被阻塞。参数设为False则返回False不会被阻塞
print(semaphore._value)
semaphore.release() #第一次释放
print(semaphore._value)
semaphore.release()
print(semaphore._value)
semaphore.release() #第三次释放
print(semaphore._value)
semaphore.release() #第四次释放,由于现在已经没有资源被占用所以会抛出异常
条件变量使用示例代码以及相关作用
条件变量:由于有些复杂问题互斥锁搞不定了。Python
提供的Condition
对象提供了对复杂线程同步问题的支持。Condition
被称为条件变量,除了提供与Lock
类似的acquire
和release
方法外,还提供了wait
和notify
方法。
定义参考:[python] 线程间同步之条件变量Condition - 简书
"""条件变量"""
import threading
import time
def run(x):
#lock.acquire()
con.acquire()#使用条件变量
print(f'线程{x}')
con.notify() #结束上一个进程的等待状态 启动上一个进程
print(f'线程{x}挂起')
con.wait()#条件变量中的等待,线程中的等待函数为join()
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()#条件变量中的等待,线程中的等待函数为join()
time.sleep(1)
print(f'线程{x}再次启动')
con.notify()#程序运行完之后 启动上一个线程
con.release()#资源释放
#lock.release()
if __name__=='__main__':
#lock = threading.Lock()
con = threading.Condition()#条件变量
# for i in range(10):
# 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()
实例方法:
acquire([timeout])/release(): 调用关联的锁的相应方法。
wait([timeout]): 调用这个方法将使线程进入Condition的等待池等待通知,并释放锁。
使用前线程必须已获得锁定,否则将抛出异常。
notify(): 调用这个方法将从等待池挑选一个线程并通知,收到通知的线程将自动调用
acquire()尝试获得锁定(进入锁定池);其他线程仍然在等待池中。调用这个方法不会
释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
notifyAll(): 调用这个方法将通知等待池中所有的线程,这些线程都将进入锁定池
尝试获得锁定。调用这个方法不会释放锁定。使用前线程必须已获得锁定,否则将抛出异常。
事件实例代码及其相关作用
同进程的一样,线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其 他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在 初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行。
"""事件"""
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(3)
if __name__ =='__main__':
event = threading.Event() #创建事件
car1 = threading.Thread(target=car)
car1.start()
set_e = threading.Thread(target=set_event)
set_e.start()
Event的几种方法:
threading.Event(): 创建事件
event.isSet():返回event的状态值;
event.wait():如果 event.isSet()==False将阻塞线程;
event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度;
event.clear():恢复event的状态值为False。
本次任务:使用类继承的方式,实现信号量、事件功能操作。具体案例:第一个线程中获取当前时间,判断当前时间3秒之后,触发“事件”对象。在另一个线程中,作为数学考试结束的判断变量,否则一直处于考试中,并打印
import threading
import time
def shijian():
while True:
day1=time.strftime("%Y %m %d %H:%M:%S", time.localtime())# 获取 格式化成2016-03-20 11:45:39形式 的本地时间 并赋值给day1
day11=time.mktime(time.strptime(day1,"%Y %m %d %H:%M:%S"))# 将格式字符串转换为时间戳 并赋值给day11
time.sleep(3)#延迟3秒
day2=time.strftime("%Y %m %d %H:%M:%S", time.localtime())# 获取 格式化成2016-03-20 11:45:39形式 的本地时间 并赋值给day2
day22 = time.mktime(time.strptime(day2, "%Y %m %d %H:%M:%S"))# 将格式字符串转换为时间戳 并赋值给day22
day=day22-day11 #将时间差赋值给day
print(day)#输出day
if day==3: #如果时间间隔为3秒
set_event() # 创建事件
print("事件1触发成功")
break #跳出循环
else:
print("事件1触发失败")
def kaoshi():
while True:
time.sleep(1) #延时1秒
if event.is_set():#如果设置了事件
print("考试结束")
break #跳出循环
else:
print("正在考试中")
def set_event():
while True:
event.set()#设置事件
time.sleep(1) #延迟1秒
if event.is_set(): # 如果间隔事件为3秒
print("事件2触发成功")
break #跳出循环
else:
print("事件2触发失败")
# event.clear() #把所设置的事件清除
# time.sleep(3)
# 用类来创建多线程
#创建类 继承自threading.Thread
class MyThread(threading.Thread):
def __init__(self): #初始化 并传递参数
super(MyThread, self).__init__() #继承自父类的初始化
# 启动线程的函数 函数名必须是run
def run(self):
shijian() #进入shijian()函数
# 用类来创建多线程
#创建类 继承自threading.Thread
class mythread(threading.Thread):
def __init__(self): #初始化 并传递参数
super(mythread, self).__init__() #继承自父类的初始化
# 启动线程的函数 函数名必须是run
def run(self):
kaoshi() #进入kaoshi()函数
#程序主入口
if __name__ == '__main__':
event = threading.Event() # 创建事件
# semaphore = threading.Semaphore(2) # 将信号量实例化 括号里填写同时执行线程的个数
#调用类多线程
r1=MyThread() #实例化所创建的类
r1.start() # 启动多线程
r2=mythread() #实例化所创建的类
r2.start() # 启动多线程