Python多线程使用random.shuffle导致列表结果异常问题
多线程环境下random.shuffle打乱列表出现值缺失/重复的问题
这个问题我之前也踩过坑!核心原因很明确:random模块的shuffle方法不是线程安全的,numpy的shuffle同理。你现在让多个线程同时对同一个列表对象执行shuffle操作,就会触发线程竞争——比如线程A正在交换列表元素的中途,线程B突然切入修改同一个位置,结果就是元素被覆盖、重复甚至丢失。
为什么会偶发?
这完全取决于系统的线程调度时机:如果某个线程能完整跑完shuffle再切换到下一个线程,结果就正常;但如果调度器在shuffle的执行过程中强行切换线程,两个线程的修改操作互相干扰,就会出现异常结果。
解决方案
最推荐的是让每个线程操作列表的独立副本,这样彻底避免多线程竞争,效率也更高:
#!/usr/bin/python3 -tt import random from threading import Thread class Th(Thread): def __init__ (self, num, c): Thread.__init__(self) self.c = c self.num = num def run(self): # 复制原列表得到独立副本,每个线程打乱自己的副本 shuffled_c = self.c.copy() random.shuffle(shuffled_c) for i in shuffled_c: print(f"{self.num} - {i}") def main(): # with语句会自动关闭文件,无需手动调用close with open('file.txt', 'r') as cmddb: c = [cmd.strip() for cmd in cmddb] print('ref:') for i in c: print(i) print('----') for thread_number in range(5): thread = Th(thread_number, c) thread.start() if __name__ == '__main__': main()
如果因为特殊需求必须共享原列表(非常不推荐),那就要用锁来同步shuffle操作,确保同一时间只有一个线程能修改列表:
#!/usr/bin/python3 -tt import random from threading import Thread, Lock # 创建全局锁,控制shuffle操作的互斥执行 shuffle_lock = Lock() class Th(Thread): def __init__ (self, num, c): Thread.__init__(self) self.c = c self.num = num def run(self): global shuffle_lock # 加锁后执行shuffle,避免并发修改冲突 with shuffle_lock: random.shuffle(self.c) # 注意:这里打印的是刚打乱后的原列表,后续线程会再次打乱它 for i in self.c: print(f"{self.num} - {i}") def main(): with open('file.txt', 'r') as cmddb: c = [cmd.strip() for cmd in cmddb] print('ref:') for i in c: print(i) print('----') for thread_number in range(5): thread = Th(thread_number, c) thread.start() if __name__ == '__main__': main()
关于numpy的情况
numpy的np.random.shuffle同样不是线程安全的,解决思路完全一致:要么给shuffle加锁,要么每个线程复制数组副本,并用独立的随机生成器打乱(避免全局随机状态被多线程干扰):
import numpy as np from threading import Thread class Th(Thread): def __init__ (self, num, arr): Thread.__init__(self) self.arr = arr self.num = num def run(self): # 复制数组得到独立副本 shuffled_arr = self.arr.copy() # 每个线程用自己的随机生成器,避免全局状态冲突 rng = np.random.RandomState() rng.shuffle(shuffled_arr) for i in shuffled_arr: print(f"{self.num} - {i}") def main(): arr = np.array([10, 20]) print('ref:') print(arr) print('----') for num in range(5): thread = Th(num, arr) thread.start() if __name__ == '__main__': main()
总的来说,尽量不要让多线程共享并修改同一个可变对象,操作副本是最省心、高效的方式~
内容的提问来源于stack exchange,提问作者Juliano
相关产品推荐
相关产品推荐

