python——多线程编程(threading)
多线程是指在一个程序中同时执行多个独立的任务或操作。每个任务或操作都是由一个单独的线程来执行,而这些线程共享程序的资源和内存空间。Python 通过 threading
模块提供了对多线程的支持,使用 threading
模块可以方便地创建和管理线程。以下是几个使用 Python threading
库的案例,以帮助学习python的多线程编程
案例 1:基本的多线程任务
这个案例展示了如何创建多个线程来执行简单的任务
import threading
import timedef print_numbers():for i in range(5):time.sleep(1)print(i)def print_letters():for letter in 'abcde':time.sleep(1.5)print(letter)# 创建线程
t1 = threading.Thread(target=print_numbers)
t2 = threading.Thread(target=print_letters)# 启动线程
t1.start()
t2.start()# 等待线程完成
t1.join()
t2.join()print("所有线程执行完毕")
输出示例 :
案例 2:带参数的线程
这个案例展示了如何向线程传递参数
import threading
import timedef task(id, delay):print(f"任务 {id} 开始")time.sleep(delay)print(f"任务 {id} 完成")# 创建多个线程
threads = []
for i in range(1, 6):t = threading.Thread(target=task, args=(i, i * 0.5))threads.append(t)t.start()# 等待所有线程完成
for t in threads:t.join()print("所有任务完成")
输出示例 :
案例 3:线程池
线程池是一种预先创建一组线程并保存在内存中的线程管理方式。当有任务到来时,线程池会从预创建的线程中选择一个执行任务,避免了线程的创建和销毁开销。这个案例展示了如何使用 ThreadPoolExecutor
来管理线程池。
使用场景
1. 线程池:适用于 I/O 密集型任务,如网络请求、文件读写等。线程池适用于异步编程模型,可以有效地处理 I/O 密集型任务。
2. 直接使用线程:适用于简单的并发任务,任务数量较少。
import concurrent.futures
import timedef worker(id):print(f"工作线程 {id} 开始")time.sleep(2)print(f"工作线程 {id} 完成")return f"结果来自 {id}"# 创建线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:# 提交任务futures = [executor.submit(worker, i) for i in range(5)]# 获取结果for future in concurrent.futures.as_completed(futures):print(future.result())print("主线程继续运行")
案例 4:线程同步
为了确保代码的线程安全性,应该使用锁来保护对共享资源的访问。锁可以确保在同一时刻只有一个线程能够访问共享资源,从而避免竞态条件。这个案例展示了如何使用锁(Lock
)来避免线程间的竞争条件。
下面的代码为对counter执行1000000次加一的操作,
如果不设置锁保证线程同步,代码如下:
import threading# 共享资源
counter = 0def increment():global counterfor _ in range(1000000):counter += 1# 创建多个线程
threads = []
for _ in range(10):t = threading.Thread(target=increment)threads.append(t)t.start()# 等待所有线程完成
for t in threads:t.join()print(f"不加锁的最终计数器值:{counter}")
输出如下:
不限制线程对counter的获取会获得不同的结果。
因此需要加锁,确保只有拿到钥匙的线程才能够对counter进行加一的操作,修改代码如下:
import threading# 共享资源
counter = 0
lock = threading.Lock()def increment():global counterfor _ in range(1000000):with lock:counter += 1# 创建多个线程
threads = []
for _ in range(10):t = threading.Thread(target=increment)threads.append(t)t.start()# 等待所有线程完成
for t in threads:t.join()print(f"加锁的最终计数器值:{counter}")
输出如下:
案例 5:守护线程
守护线程(Daemon Thread)是一种特殊的线程,主要用于在后台执行一些不需要用户直接干预的任务。当所有非守护线程(用户线程)结束时,程序会自动退出,无论守护线程是否完成其任务。这个案例展示了如何使用守护线程。
使用场景
-
日志记录:后台线程不断记录系统的运行状态。
-
数据抓取:周期性地从外部数据源获取数据。
-
定时任务:定时清理临时文件、缓存等
import threading
import timedef daemon_task():while True:print("守护线程运行中...")time.sleep(1)def main_task():print("主线程开始")time.sleep(5)print("主线程结束")# 创建守护线程
daemon = threading.Thread(target=daemon_task)
daemon.daemon = True # 设置为守护线程# 创建主线程
main = threading.Thread(target=main_task)
main.start() # 开始主线程daemon.start() # 开始守护线程main.join() # 等待主线程完成
输出示例:
主线程结束后,守护线程会自动退出!
案例 6:threading.Event事件对象
在 Python 的 threading
模块中,Event
(事件对象)是一种用于线程间通信的简单机制。它提供了一种线程同步的方式,通过一个内部标志来管理线程的执行状态。
事件对象的主要方法
-
is_set()
:当内置标志为True
时返回True
。 -
set()
:将标志设为True
,并通知所有处于等待阻塞状态的线程恢复运行状态。 -
clear()
:将标志设为False
。 -
wait([timeout])
:如果标志为True
将立即返回,否则阻塞线程至等待阻塞状态,等待其他线程调用set()
。可以通过timeout
参数指定超时时间。
使用场景
-
线程间的启动/停止信号:一个线程可以等待另一个线程发出开始或停止的信号。
-
任务完成通知:一个线程完成一项任务后,通过设置事件通知其他相关线程。
-
线程间的信号传递:通知其他线程某个条件已满足。
-
同步启动:等待所有线程准备就绪后同时开始。
-
定期检查:在循环中使用带超时的
wait
进行周期性检查。 -
资源就绪通知:等待某个资源准备完毕。
6.1 视频的上传和下载:
import threading
import time# 创建一个事件对象
event = threading.Event()def download_video():print("开始下载视频")time.sleep(5) # 模拟下载耗时print("视频下载完成,通知播放")event.set()def play_video():print("等待视频下载")event.wait()print("开始播放视频")# 创建并启动线程
download_thread = threading.Thread(target=download_video)
play_thread = threading.Thread(target=play_video)download_thread.start()
play_thread.start()download_thread.join()
play_thread.join()
输出示例:
6.2:定期检查
import threading
import time# 创建一个事件对象
event = threading.Event()def worker():while True:print("工人等待开始信号")event.wait(timeout=2) # 每2秒检查一次if event.is_set():print("工人开始工作")event.clear() # 清除事件else:print("工人继续等待")# 创建并启动线程
thread = threading.Thread(target=worker)
thread.start()# 模拟主线程设置事件
time.sleep(5)
event.set() # 发送开始信号thread.join()
输出示例:
6.3 通知任务完成
import threading
import time
import random# 创建一个事件对象
event = threading.Event()
data = []def data_generator():global dataprint("数据生成器开始生成数据")for _ in range(5):time.sleep(random.randint(1, 2))data.append(random.randint(1, 100))print("数据生成完成,通知处理线程")event.set()def data_processor():print("数据处理器等待数据")event.wait()print("开始处理数据:", data)# 创建并启动线程
thread1 = threading.Thread(target=data_generator)
thread2 = threading.Thread(target=data_processor)thread1.start()
thread2.start()thread1.join()
thread2.join()
输出示例: