Eigen库SimplicialLLT多线程调用返回结果不正确问题求助
问题原因
你的问题核心出在subProg1内部声明的两个static变量:
static vector<SparseMatrix<double>> Kmat(NROW, SparseMatrix<double> (nodes*3,nodes*3)); static vector<SimplicialLLT<SparseMatrix<double>>> Kmat_LLT(Kmat.size());
函数内部的静态变量是全局共享的,所有线程都会操作同一份vector实例,直接带来两个风险:
- 静态变量的初始化时机不确定:C++11之后虽然保证静态变量的构造过程是线程安全的,只会执行一次,但构造时使用的
nodes参数是第一个进入subProg1的线程传入的值,如果你实际场景中不同线程传入的nodes可能不同,就会导致矩阵尺寸不符合预期。哪怕你现在所有线程传入的nodes都是20000,初始化完成前其他线程就开始写入Kmat或者调用compute的行为也属于未定义行为。 - 隐含的竞态风险:虽然你给每个线程分配了独立的索引
IROW,理论上不会互相覆盖,但如果你的实际代码中填充Kmat[IROW]的逻辑用到了任何共享变量没有加锁,或者Eigen内部的稀疏矩阵操作存在隐含的共享状态(比如内存池的线程安全问题),都会导致计算结果错误。
串行场景下之所以结果正常,是因为线程依次执行,第一个线程进入函数完成静态变量的完整初始化,后续线程直接使用已经构造好的vector,且同一时间只有一个线程读写vector元素,没有竞态,因此结果正确;并行场景下多个线程同时进入函数,静态变量初始化和后续的读写操作可能重叠,导致未定义行为,结果异常。
解决方案
方案1:完全避免变量共享(简单通用)
移除两个变量的static修饰符,让每个线程执行subProg1时独立创建自己的矩阵和LLT分解对象,完全规避共享风险:
void subProg1(int IROW, int nodes) { // 去掉static,每个线程独有一份实例 vector<SparseMatrix<double>> Kmat(NROW, SparseMatrix<double> (nodes*3,nodes*3)); vector<SimplicialLLT<SparseMatrix<double>>> Kmat_LLT(Kmat.size()); // 填充Kmat[IROW]的逻辑 Kmat_LLT[IROW].compute(Kmat[IROW]); // 后续处理逻辑 }
方案2:提前初始化复用对象(性能更优)
如果你需要复用矩阵和LLT对象减少内存分配开销,可以在主线程启动子线程之前,提前完成两个vector的初始化,再给每个线程传入对应索引元素的引用,完全规避初始化阶段的竞态:
// 主线程中提前初始化 int main() { int imax = 4; int nodes = 20000; vector<SparseMatrix<double>> Kmat(NROW, SparseMatrix<double> (nodes*3,nodes*3)); vector<SimplicialLLT<SparseMatrix<double>>> Kmat_LLT(Kmat.size()); std::thread threadpointer[4]; for (int ii=0;ii<imax;ii++) { // 传入对应元素的引用给子线程 threadpointer[ii] = std::thread(subProg1,ii, std::ref(Kmat[ii]), std::ref(Kmat_LLT[ii])); } for (int ii=0;ii<imax;ii++) { threadpointer[ii].join(); } } // subProg1修改为接收对应实例的引用 void subProg1(int IROW, SparseMatrix<double>& Kmat, SimplicialLLT<SparseMatrix<double>>& Kmat_LLT) { // 填充Kmat的逻辑 Kmat_LLT.compute(Kmat); // 后续处理逻辑 }
校验方法
你可以在compute调用后添加校验,快速定位分解失败的问题:
Kmat_LLT[IROW].compute(Kmat[IROW]); if(Kmat_LLT[IROW].info() != Eigen::Success) { cerr << "Thread " << IROW << " LLT decomposition failed!" << endl; }
如果多线程场景下出现分解失败的提示,说明你填充Kmat的逻辑存在竞态,需要检查填充过程是否用到了未加锁的共享变量。
内容的提问来源于stack exchange,提问作者pk68
相关产品推荐
相关产品推荐

