多线程合并4个文本文件字符串异常:仅3个文件成功合并
多线程合并文件内容丢失问题排查
问题描述
读取4个含字符串的文本文件,通过单线程与多线程方式合并为单个string vector后排序并对比速度。单线程逻辑与排序算法运行正常,但多线程场景下仅3个文件的内容被合并到vector中,包含最多字符串的第4个文件内容丢失。
相关代码
多线程处理函数
void MultiThreaded(vector<string> filefist, ESort sort) { vector<string> stringlist; vector<thread> workerThreads(filelist.size()); for (unsigned int i = 0; i < filelist.size() ; ++i) { workerThreads[i] = thread(FileRead, filelist[i],&stringlist); } for (unsigned int i = 0; i < filelist.size(); i++){ workerThreads[i].join(); } stringlist = Sort(stringlist, sort); }
文件读取函数
void FileRead(string fname, vector<string>* outlist) { *outlist = ReadFile(fname); }
注:排序代码为单多线程共用。
问题根源
赋值操作导致内容覆盖
每个线程调用FileRead时,通过*outlist = ReadFile(fname)将当前文件内容直接赋值给目标vector,这会完全替换vector原有内容,而非追加。多线程并发执行该操作时,会互相覆盖结果,最终stringlist仅保留最后完成的线程读取的内容。你观察到的"3个文件内容被合并"是线程竞争导致的随机结果,本质是赋值操作的冲突覆盖。vector非线程安全容器
即使改为追加操作(如push_back/insert),直接在多线程中修改同一个vector也会触发数据竞争——vector的内部扩容、指针移动等操作在并发场景下会出现未定义行为,可能导致数据丢失、程序崩溃。
修复方案
方案1:独立读取后合并(推荐,无锁开销)
让每个线程独立读取文件内容到专属vector,主线程等待所有线程完成后再合并结果,彻底避免线程竞争:
// 修改文件读取函数,返回独立的结果vector vector<string> FileRead(string fname) { return ReadFile(fname); } void MultiThreaded(vector<string> filelist, ESort sort) { vector<vector<string>> threadResults(filelist.size()); vector<thread> workerThreads; for (unsigned int i = 0; i < filelist.size(); ++i) { workerThreads.emplace_back([i, &filelist, &threadResults]() { threadResults[i] = FileRead(filelist[i]); }); } // 等待所有线程执行完毕 for (auto& t : workerThreads) { t.join(); } // 合并所有线程的结果 vector<string> stringlist; for (auto& result : threadResults) { stringlist.insert(stringlist.end(), result.begin(), result.end()); } stringlist = Sort(stringlist, sort); }
方案2:互斥锁保护追加操作(适合实时合并场景)
如果需要在线程中直接追加内容到总vector,需用互斥锁保护修改操作,避免数据竞争:
#include <mutex> std::mutex vecMutex; void FileRead(string fname, vector<string>* outlist) { vector<string> tempContent = ReadFile(fname); // 加锁后安全追加内容 std::lock_guard<std::mutex> lock(vecMutex); outlist->insert(outlist->end(), tempContent.begin(), tempContent.end()); } void MultiThreaded(vector<string> filelist, ESort sort) { vector<string> stringlist; vector<thread> workerThreads(filelist.size()); for (unsigned int i = 0; i < filelist.size() ; ++i) { workerThreads[i] = thread(FileRead, filelist[i],&stringlist); } for (unsigned int i = 0; i < filelist.size(); i++){ workerThreads[i].join(); } stringlist = Sort(stringlist, sort); }
说明
方案1的性能更优,因为线程间无锁竞争,仅在主线程执行一次合并操作;方案2适合需要实时合并内容的场景,但锁会带来一定性能损耗,文件内容越多损耗越明显。
内容的提问来源于stack exchange,提问作者haize233
相关产品推荐
相关产品推荐

