设计思路
- 使用队列替代直接访问列表:利用
queue.Queue
来管理对共享数据的访问。Queue
本身是线程安全的,内部实现了锁机制来保证数据一致性,减少手动加锁解锁的开销。
- 边界检查封装:将对列表元素的访问逻辑封装在函数中,在函数内部进行边界检查,确保每次访问都是安全的。
- 生产者 - 消费者模型:使用生产者 - 消费者模型,线程作为生产者将任务(如对列表的访问请求)放入队列,而一个或多个消费者线程从队列中取出任务并执行,这样可以解耦线程对共享列表的直接操作。
关键代码片段及解释
import queue
import threading
# 共享大列表
large_list = list(range(1000000))
# 任务队列
task_queue = queue.Queue()
# 边界检查及列表访问函数
def access_list(index):
if 0 <= index < len(large_list):
return large_list[index]
else:
raise IndexError("Index out of range")
# 消费者线程函数
def consumer():
while True:
index = task_queue.get()
try:
result = access_list(index)
print(f"Accessed element: {result}")
except IndexError as e:
print(f"Error: {e}")
task_queue.task_done()
# 创建并启动消费者线程
consumer_thread = threading.Thread(target=consumer)
consumer_thread.daemon = True # 设置为守护线程,主线程结束时自动退出
consumer_thread.start()
# 模拟生产者(其他线程)向队列中添加任务
def producer():
for i in range(10): # 模拟10次访问请求
task_queue.put(i)
# 创建并启动生产者线程
producer_thread = threading.Thread(target=producer)
producer_thread.start()
# 等待所有任务完成
task_queue.join()
large_list
:定义共享的大列表。
task_queue
:创建一个任务队列,用于线程间传递对列表访问的请求。
access_list
函数:封装了对列表的访问逻辑,并进行边界检查。如果索引在合法范围内,返回列表元素;否则抛出IndexError
。
consumer
函数:消费者线程的执行函数,不断从任务队列中获取任务(列表索引),调用access_list
函数进行访问,并处理可能出现的索引错误。处理完任务后,调用task_queue.task_done()
通知队列任务已完成。
producer
函数:模拟生产者线程,向任务队列中添加任务(这里简单地添加索引值)。
- 线程启动与等待:创建并启动消费者线程和生产者线程,最后通过
task_queue.join()
等待所有任务完成。消费者线程设置为守护线程,主线程结束时,消费者线程也会自动结束。