定时器
定时器用处
程序中,经常用到这种,就是需要固定时间执行的,或者需要每隔一段时间执行的。这里经常用的就是Timer定时器。Thread 类有一个 Timer子类,该子类可用于控制指定函数在特定时间内执行一次。
代码实例
import threading
import time
def run():
print('定时器启动')
print(threading.current_thread()) # 显示当前线程的方法
timer = threading.Timer(5,run) # 使用定时器,调用run方法,且时间为5秒,每5秒输出一次
timer.start()
if __name__=='__main__':
t1 = threading.Timer(5,function=run) # 使用定时器,调用run方法,且时间为5秒,每5秒输出一次
t1.start()
while True:
time.sleep(5)
print('主线程')
运行结果
队列
队列的概念
队列是线性的集合,对于队列来说,插入限制在一端(队尾),删除限制在另一端(队头)。队列支持先进先出(FIFO)的协议。
我们在后面会提到一种特殊的队列——优先队列,在优先队列中,具有较高优先级的项会在那些具有较低优先级的项之前弹出,而具有相同优先级的项,则会按照先进先出(FIFO)的顺序弹出。
队列中含有的方法
代码示例
import queue
q = queue.Queue(maxsize=3)
q.put((1,'你好1'))
q.put((3,'你好3'))
print(q.full())# 判断是否放满了
q.put((2,'你好'))
q.full()
print(q.full())# 判断是否放满了
print(q.get())
print(q.empty()) # 判断是否为空 --fales,只取了1个
print(q.get())
print(q.get())
print(q.empty()) # 判断是否为空 --true,三个都取完了
print(q.qsize())
运行结果
import queue
import threading
import time
q = queue.Queue(maxsize=10)
# 相当于生产者,生产视频
def put_in():
count = 1
while True:
q.put(f'视频{count}')
print(f'up主录制了视频{count}')
count += 1
time.sleep(1)
# 相当于消费者,霸占视频
def get_out(name):
while True:
print(f'{name}霸占了{q.get()}')
time.sleep(1)
if __name__=='__main__':
p = threading.Thread(target=put_in) # 生产者线程
p.start()
g = threading.Thread(target=get_out,args=('小潮',)) # 消费者线程
g.start()
g = threading.Thread(target=get_out, args=('海皇',)) # 消费者线程
g.start()
运行结果
线程池
线程池的作用
由于线程预先被创建并放入线程池中,同时处理完当前任务之后并不销毁而是被安排处理下一个任务,因此能够避免多次创建线程,从而节省线程创建和销毁的开销,能带来更好的性能和系统稳定性。
线程池方法
线程池的基类是 concurrent.futures 模块中的 Executor,Executor 提供了两个子类,即 ThreadPoolExecutor 和 ProcessPoolExecutor,其中 ThreadPoolExecutor 用于创建线程池,而 ProcessPoolExecutor 用于创建进程池。
如果使用线程池/进程池来管理并发编程,那么只要将相应的 task 函数提交给线程池/进程池,剩下的事情就由线程池/进程池来搞定。
Exectuor 提供了如下常用方法:
submit(fn, *args, **kwargs):将 fn 函数提交给线程池。*args 代表传给 fn 函数的参数,*kwargs 代表以关键字参数的形式为 fn 函数传入参数。
map(func, *iterables, timeout=None, chunksize=1):该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
shutdown(wait=True):关闭线程池。
程序将 task 函数提交(submit)给线程池后,submit 方法会返回一个 Future 对象,Future 类主要用于获取线程任务函数的返回值。由于线程任务会在新线程中以异步方式执行,因此,线程执行的函数相当于一个“将来完成”的任务,所以 Python 使用 Future 来代表。
Future 提供了如下方法:
cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。
cancelled():返回 Future 代表的线程任务是否被成功取消。
running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。
done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。
add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。
在用完一个线程池后,应该调用该线程池的 shutdown() 方法,该方法将启动线程池的关闭序列。调用 shutdown() 方法后的线程池不再接收新任务,但会将以前所有的已提交任务执行完成。当线程池中的所有任务都执行完成后,该线程池中的所有线程都会死亡。
代码示例
'''
线程池能够提高性能,防止大量的线程而导致系统变慢,可以更简单的创建线程,适用于突发需要大量的线程,而线程存在时间短的场景
'''
from concurrent.futures import ThreadPoolExecutor
import time
def run(x):
print(f'线程{x}')
time.sleep(5)
return x * 10
def callback(future):
print(future.result())
if __name__=='__main__':
# pool = ThreadPoolExecutor(max_workers=2) # 同时工作的只能是两个线程,当其中的线程完成工作后,才能够启动后便的线程
with ThreadPoolExecutor(max_workers=2) as pool: # 这种方式,就可以不手动的关闭线程池
# 启动三个线程
future1 = pool.submit(run,1)
future2 = pool.submit(run, 2)
future3 = pool.submit(run, 3)
# 查看线程1是否完成
print(future1.done())
time.sleep(3)
# 查看线程2是否完成
print(future2.done())
future1.add_done_callback(callback())
future3.add_done_callback(callback())
# 获取线程1的返回值
print(future1.result())
# 获取线程3的返回值
print(future3.result(timeout=1)) # 等待一秒钟,如果等待一秒之后还是没有获取到结果,就会产生报错信息
pool.shutdown() # 关闭线程池 ,等待已经启动的线程结束,才会关闭线程池。当关闭后再次添加线程池,就会产生报错信息
print('程序完成')
# 也会阻塞主线程
result = pool.map(run,[7,8,9]) # 启动线程7、8、9
print(result)
for i in result:
print(i)
print('程序彻底结束了')
参考
线程池方法队列方法