信号量、Event、定时器
一 信号量
信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行,如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群路人争抢公共厕所,公共厕所有多个坑位,这意味着同一时间可以有多个人上公共厕所,但公共厕所容纳的人数是一定的,这便是信号量的大小
from threading import Thread, Semaphore, currentThread import time, random sm = Semaphore(5) # 锁的个数 def task(): # sm.acquire() # print("%s in " % currentThread().getName()) # sm.release() # 简写为以下形式 with sm: print("%s in " % currentThread().getName()) time.sleep(random.randint(1, 3)) if __name__ == "__main__": for i in range(10): # 10个人去抢 t = Thread(target=task) t.start()
解析
Semaphore管理一个内置的计数器,
每当调用acquire()时内置计数器-1;
调用release() 时内置计数器+1;
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其他线程调用release()。
二 Event
线程的一个关键特性是每个线程都是独立运行且状态不可预测。如果程序中的其他线程需要通过判断某个线程的状态来确定自己下一步的操作,这时线程同步问题就会变得非常棘手。为了解决这些问题,我们需要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它允许线程等待某些事件的发生。在初始情况下,Event对象中的信号标志被设置为假。如果有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程如果将一个Event对象的信号标志设置为真,它将唤醒所有等待这个Event对象的线程。如果一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
from threading import Event event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
例如,有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,如果连接不成功,都会去尝试重新连接。那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作
from threading import Thread, Event import time event = Event() # event.wait() # 在此等着 # event.set() # 等待结束 def student(name): print("学生%s正在听课" % name) event.wait(2) # 可以设置超时时间,即使没有接收到set的信号,也可以继续往后进行 print("学生%s正在课件活动" % name) def teacher(name): print("%s正在上课" % name) time.sleep(7) event.set() if __name__ == "__main__": stu1 = Thread(target=student, args=("alex",)) stu2 = Thread(target=student, args=("black",)) stu3 = Thread(target=student, args=("jack",)) t1 = Thread(target=teacher, args=("nico",)) stu1.start() stu2.start() stu3.start() t1.start()
ftp中
from threading import Thread, Event, currentThread import time event = Event() def conn(): # 连接服务端 n = 0 while not event.is_set(): if n == 3: print("%s try too many times" % currentThread().getName()) return print("%s try %s" % (currentThread().getName(), n)) event.wait(0.5) # 超时时间 print("%s is connected" % currentThread().getName()) def check(): # 检测服务端是否正常进行 print("%s is checking" % currentThread().getName()) time.sleep(5) event.set() if __name__ == "__main__": for i in range(3): t = Thread(target=conn) t.start() t = Thread(target=check) t.start()
三 定时器
定时器,指定n秒后执行某操作
from threading import Timer def task(name): print("hello %s" % name) t = Timer(5,task,args=("alex", )) # 5s后 t.start()
验证码
# 验证码 from threading import Timer import random class Code: def __init__(self): self.make_cache() def make_cache(self, interval=5): self.cache = self.make_code() print(self.cache) self.t = Timer(interval, self.make_cache) self.t.start() def make_code(self, n=4): res = "" for i in range(n): s1 = str(random.randint(0, 9)) s2 = chr(random.randint(65, 90)) # 字母 res += random.choice([s1, s2]) return res def check(self): while True: code = input("请输入验证码:").strip() if code.upper() == self.cache: print("验证码正确") self.t.cancel() break obj = Code() obj.check()