Python 3.5多线程报错:进程退出码-1073741819(0xC0000005)
先给你明确这个错误的本质:0xC0000005是Windows系统下的访问违规(Access Violation),简单说就是程序试图访问了不允许它操作的内存区域,结合你的多线程场景,大概率和共享资源的线程冲突、不安全的内存操作有关。下面是针对性的排查和解决方向:
1. 主线程未等待子线程完成就执行文件保存
你当前的代码启动线程后立刻调用np.savetxt,这时候子线程可能还在运行甚至还没开始修改数组,不仅会导致保存的文件不完整,更关键的是:线程后台修改数组的同时主线程在读写数组,很容易触发内存访问冲突。
修复方法:把线程对象存起来,用join()等待所有子线程执行完毕再保存文件:
if __name__ == "__main__": # ... 你的数据读取、数组初始化代码 ... # 存储线程对象 threads = [] threads.append(threading.Thread(target=calculation, args=(0,))) threads.append(threading.Thread(target=calculation, args=(6,))) threads.append(threading.Thread(target=calculation, args=(12,))) threads.append(threading.Thread(target=calculation, args=(18,))) # 启动所有线程 for t in threads: t.start() # 等待所有线程执行完成 for t in threads: t.join() # 现在再执行保存操作 np.savetxt('threshold_'+str(1)+'.csv', data_table_t1, delimiter=',', fmt='%10.5f') # ... 其他savetxt代码 ...
2. 多线程共享numpy数组的线程安全问题
numpy数组的内存是连续的,且绝大多数numpy操作都不是原子操作——多个线程同时读写同一个data_table_t1这类数组时,会出现多个线程同时修改同一块内存的情况,直接触发内存访问错误。
方案A:每个线程操作独立局部数组,最后合并(推荐)
让calculation函数返回自己计算的局部数组,主线程等待所有线程结束后合并结果,完全避免共享内存冲突:
def calculation(arg): # 初始化线程专属的局部数组 local_t1 = np.zeros((101, 10)) local_t5 = np.zeros((101, 10)) local_t10 = np.zeros((101, 10)) local_t20 = np.zeros((101, 10)) local_t40 = np.zeros((101, 10)) local_t80 = np.zeros((101, 10)) # ... 你的多层嵌套循环计算逻辑,修改局部数组 ... return local_t1, local_t5, local_t10, local_t20, local_t40, local_t80 if __name__ == "__main__": # ... 其他初始化代码 ... # 用ThreadPoolExecutor更方便处理带返回值的线程 from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers=4) as executor: results = executor.map(calculation, [0,6,12,18]) # 合并所有线程的结果到全局数组 for res in results: t1, t5, t10, t20, t40, t80 = res data_table_t1 += t1 data_table_t5 += t5 data_table_t10 += t10 data_table_t20 += t20 data_table_t40 += t40 data_table_t80 += t80 # 保存文件 # ... savetxt代码 ...
方案B:用线程锁保护数组读写
如果必须共享全局数组,用threading.Lock确保同一时间只有一个线程能操作数组:
# 全局定义线程锁 lock = threading.Lock() def calculation(arg): # ... 你的前置计算逻辑,先处理好要写入的数据 ... # 修改全局数组前加锁 with lock: # 这里写修改data_table_t1等数组的代码 data_table_t1[target_index] = calculated_value data_table_t5[target_index] = calculated_value # ... 其他数组修改操作 ...
注意:加锁会降低多线程效率,因为同一时间只有一个线程能执行锁内代码,所以优先推荐方案A。
3. 检查netCDF数据集的多线程安全性
你全局使用了netcdf_entire_dataset这个Dataset对象,如果calculation函数直接读取该对象的变量,部分旧版本的netCDF4库在多线程环境下读取数据不是线程安全的,也会触发访问违规。
修复方法:
- 升级netCDF4库:
pip install --upgrade netCDF4 - 让每个线程单独打开/关闭Dataset,避免共享对象:
def calculation(arg): # 每个线程独立打开数据集 with Dataset("F:/dataset/entire_dataset.nc", "r") as nc: rain_models = nc.variables['rain_models'] # ... 你的计算逻辑 ...
4. 排查数组越界问题
你的calculation函数有多层嵌套循环,很可能在循环中访问数组时出现索引超出范围的情况(比如rain_models[i][j]的i/j超过数组维度),这也是触发0xC0000005的常见原因。
你可以先单线程运行(只启动一个线程)测试:如果单线程没问题,那就是多线程共享资源的问题;如果单线程也出错,重点排查循环里的索引是否正确,比如打印循环的索引值,核对是否匹配数组的shape。
内容的提问来源于stack exchange,提问作者Kallol Das

