7-[多线程]-Event、定时器、队列、堆栈
1、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。
超时3s中,刷新,重新执行程序等待,信号标志
2、Event应用:连接数据库
例如,有多个工作线程尝试链接MySQL,我们想要在链接前确保MySQL服务正常才让那些工作线程去连接MySQL服务器,
如果连接不成功,都会去尝试重新连接。那么我们就可以采用threading.Event机制来协调各个工作线程的连接操作
(1)初级版本
from threading import Thread, Event, currentThread import time event = Event() def conn(): print('%s is connecting' % currentThread().getName()) event.wait() # 默认是False阻塞中, 等变为True 程序放行 print('%s is connected' % currentThread().getName()) def check(): print('%s is checking' % currentThread().getName()) time.sleep(5) event.set() # Event对象 的信号标志设置未True if __name__ == '__main__': for i in range(3): t = Thread(target=conn) t.start() t = Thread(target=check) t.start()
(2)中级版本
# Event 对象,允许线程等待某些事情的发生 from threading import Thread, Event, currentThread import time event = Event() def conn(): n = 0 while not event.is_set(): print('%s is connecting %s times' % (currentThread().getName(), n)) event.wait(0.5) if n == 3: print('%s try more times' % currentThread().getName()) break n += 1 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()
(3)课件版本
from threading import Thread,Event import threading import time,random def conn_mysql(): count=1 while not event.is_set(): if count > 3: raise TimeoutError('链接超时') print('<%s>第%s次尝试链接' % (threading.current_thread().getName(), count)) event.wait(0.5) count+=1 print('<%s>链接成功' %threading.current_thread().getName()) def check_mysql(): print('\033[45m[%s]正在检查mysql\033[0m' % threading.current_thread().getName()) time.sleep(random.randint(2,4)) event.set() if __name__ == '__main__': event=Event() conn1=Thread(target=conn_mysql) conn2=Thread(target=conn_mysql) check=Thread(target=check_mysql) conn1.start() conn2.start() check.start()
3、定时器
(1)验证码功能
from threading import Timer import time import random import string # 验证码功能 # 3s验证码刷新一次 class Code(object): # 生成一个验证码 def make_code(self): code_list = random.sample(string.ascii_letters, 4) self.ret = ''.join(code_list) print(self.ret) # >> > help(random.sample) 获取帮助 # >> > random.sample(string.ascii_letters, 4) # ['W', 'B', 'g', 'n'] # 输入一个验证码 def enter_code(self): self.make_code() choice = input('输入验证码>>>').strip() if choice.upper() == self.ret.upper(): print('验证成功') else: print('验证失败') if __name__ == '__main__': c = Code() c.enter_code()
(2)定时刷新验证码
(3)课件版本
from threading import Timer import random class Code(object): 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): ret = '' for i in range(n): s1 = str(random.randint(0, 9)) s2 = chr(random.randint(65, 90)) ret += random.choice([s1, s2]) return ret def check(self): while True: code = input('输入验证码>>>').strip() if code.upper() == self.cache: print('输入成功') self.t.cancel() break obj = Code() obj.check()
4.线程queue
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
有三种不同的用法
(1)队列:先进先出
class queue.Queue(maxsize=0) #队列:先进先出
(2)堆栈:后进先出
class queue.LifoQueue(maxsize=0) #堆栈:last in fisrt out
(3)优先级队列
class queue.PriorityQueue(maxsize=0) #优先级队列:存储数据时可设置优先级的队列