为什么Python中使用threading会导致I/O速度大幅下降?该如何解决?
Python多线程保存文件速度异常问题排查与解答
原始问题
我编写了一个函数,可将大型字典列表按每份100项的规则拆分保存到文件中。该逻辑在常规运行环境下完全正常,但我仅将其改为使用threading运行后,就出现了非常明显的速度下降。就我所知,单纯引入threading或multiprocessing不会导致I/O变慢,如果我遗漏了什么基础知识点请指出,否则还请告知如何解决运行过慢的问题?
相关代码
def savePlayerQueueToDisk(saveCopy): print("="*10) print("Application exit signal received. Caching loaded players...") import pickle i = 0 for l in chunks(saveCopy, 100): with open(CACHED_PLAYERS_DIR / f"{str(i)}", 'wb') as filehandle: pickle.dump(saveCopy, filehandle) i += 1 print(f"saved chunk {i}") import sys print("="*10) sys.exit()
def mainFunction(): # Calls a main program, where on KeyboardException, it calls the savePlayerQueueToDisk function. t2 = threading.Thread(target=main, kwargs={'ignore_exceptions': False}) t2.start()
排查过程
补充测试1
经过更多测试,我改用multiprocessing替代threading,仅给原来的线程任务开一个独立子进程,mainFunction保留在主线程运行,此时就没有出现速度下降的问题。
最终根因定位
后续经过进一步测试调试,我发现该问题实际上和multiprocessing、I/O bound都无关,是savePlayerQueueToDisk()函数第8行存在逻辑错误:原代码写的是pickle.dump(saveCopy, filehandle),实际应该是pickle.dump(l, filehandle)。每次运行函数时我都会把完整列表保存到100多个文件中,后续加载所有这些文件后又会把每份数据再复制100次存到100多个文件里,显然读写这些数据的负载会越来越高,最终失控。
解答
你遇到的速度下降本质和多线程、多进程的性能差异没有直接关系,核心触发原因就是你定位到的序列化逻辑错误:
- 错误逻辑下,你每次循环都序列化并写入整个原始大列表,而非拆分后的100项小分片。假设你的列表有10000项,本来预期只需要写入100次合计10000条数据,实际写入了100次×10000条=100万条数据,总写入量直接放大了100倍,自然会感知到速度骤降。
- 改用多进程后没有出现速度下降属于巧合:大概率是测试时你清空了之前的错误缓存文件,且没有运行后续的加载-重存逻辑,总写入量没有持续叠加,才会误以为是多进程的性能优势解决了问题。如果保留错误逻辑,就算用多进程,只要重复运行几次加载存储流程,一样会出现速度爆炸式下降的问题。
修复方案如下:
- 把
pickle.dump(saveCopy, filehandle)替换为pickle.dump(l, filehandle),修正分片写入逻辑 - 将
pickle、sys等模块的导入语句移到文件头部,避免函数每次运行时重复导入的额外开销 - 写入新缓存分片前先清空旧缓存目录,避免旧的错误分片文件和新文件混淆,导致后续加载时数据重复
内容的提问来源于stack exchange,提问作者Michael Ngo
相关产品推荐
相关产品推荐

