Python 3多线程性能异常:并行执行耗时反而更长的问题排查
嘿,这个问题我之前也碰到过,其实核心原因和磁盘IO的特性以及你的代码写法都有关系,我来一步步给你拆解:
一、为啥多线程写文件更慢?
1. 磁盘IO天生是串行瓶颈
不管是机械硬盘还是大部分SSD,同一时间只能高效处理一个写入请求。当你开两个线程并行写的时候,操作系统不得不频繁切换线程来分配磁盘资源,这种上下文切换的开销会直接抵消甚至超过并行带来的收益,反而比串行一次写完更慢。
2. 频繁的系统调用放大了开销
看你的代码,循环里做了1000万次f.write()调用——每次write都是一次系统调用(从用户态切换到内核态),这个本身就有不小的开销。而多线程情况下,线程切换+多次系统调用的叠加,会让这个开销变得更大。
3. 同一文件的写入竞争(如果是写同一个文件的话)
如果两个线程写的是同一个文件,哪怕用的是追加模式'a',操作系统为了保证文件写入的原子性,会在底层加锁。两个线程会互相等待对方释放锁,这就相当于变相的串行,还多了线程切换的成本,自然比真正的串行更慢。
二、怎么优化?
1. 先缓存数据,减少系统调用次数
把小批量的写入先缓存到内存里,一次性写入文件,这能把1000万次系统调用降到1次,效率会提升几个数量级。修改后的代码大概是这样:
import timeit from threading import Thread def write_File(fName): start = timeit.default_timer() print(f'writing to {fName}!') # 用列表缓存所有内容(比字符串拼接更高效) content_buffer = [] for i in range(0, 10000000): content_buffer.append("aadadadadadadadadadadadada" + str(i)) # 一次性写入文件 with open(fName, 'a') as f: f.write('\n'.join(content_buffer)) # 按行分隔,也可以直接拼接成大字符串 end = timeit.default_timer() print(f"{fName} 耗时: {end - start:.2f}秒") print('Fn exit!') # 测试串行 start_total = timeit.default_timer() write_File("test1.txt") write_File("test2.txt") print(f"串行总耗时: {timeit.default_timer() - start_total:.2f}秒") # 测试并行 start_total = timeit.default_timer() t1 = Thread(target=write_File, args=("test1.txt",)) t2 = Thread(target=write_File, args=("test2.txt",)) t1.start() t2.start() t1.join() t2.join() print(f"并行总耗时: {timeit.default_timer() - start_total:.2f}秒")
2. 避免多线程写同一个文件
如果必须用多线程,尽量让每个线程写不同的文件——如果是SSD的话,可能能并行处理不同文件的写入,这时并行的耗时会接近串行的单线程耗时;如果是机械硬盘,提升可能有限,但至少不会比串行慢。
3. 写同一个文件时,优先用串行
如果最终要合并成一个文件,不如先让多线程写不同的临时文件,最后再串行合并,这样既利用了多线程的优势,又避免了同一文件的写入竞争。
总结
你的核心问题不是多线程本身,而是频繁的小数据写入和磁盘IO的串行特性导致的。先优化写入方式(缓存批量写入),再根据场景选择串行或多线程,就能解决耗时反而更长的问题。
内容的提问来源于stack exchange,提问作者FIre Panda

