C++线程池bind任务按引用传参更新值的正确方法
问题根因
- 核心传参错误:
std::bind默认对传入参数做值拷贝,你当前任务lambda的参数全为值类型,提交任务时会把matrix/x/b/new_x全部复制一份独立副本,线程内修改的是副本的new_x,外层原始向量不会有任何改动,副本在线程执行完任务后就直接销毁。 - 同步逻辑缺失:你提交完一轮任务后立刻调用
print_vector,没有等待线程池把所有任务执行完成,哪怕传参逻辑正确,此时任务可能还在队列中等待调度,读到的自然还是初始的全0值。 - 计算逻辑bug:循环内
sum每次都是直接赋值而非累加,最终只会保留j = n-1项的计算结果,前面的所有项都会丢失;且sum定义为int类型,会丢失浮点数计算精度。
修复方案
1. 正确传递引用参数
std::bind不会自动识别参数需要按引用传递,必须手动用std::ref(普通引用)、std::cref(常量引用)包裹需要传引用的参数,同时修改任务lambda的参数类型为对应引用:
// 先在迭代循环外层定义同步变量 int task_count = 0; mutex task_mtx; condition_variable task_cond; // 修正后的任务函数:不需要修改的参数用const引用避免拷贝,需要写入的new_x用非const引用 auto f = [&](const vector<vector<double>>& matrix, const vector<double>& x, const vector<double>& b, vector<double>& new_x, int i, int n){ double sum = 0.0; // sum改为double类型避免精度丢失 for (int j = i + 1; j < n; j++) { sum += matrix[i][j] * x[j]; // 改为+=累加所有项 } new_x[i] = (b[i] - sum) / matrix[i][i]; // 任务完成后计数器递减,通知主线程 { lock_guard<mutex> lock(task_mtx); task_count--; } task_cond.notify_one(); };
提交任务时,用std::ref/std::cref包裹引用参数:
auto fx = bind(f, cref(matrix), cref(x), cref(b), ref(new_x), i, n);
2. 增加轮次同步逻辑
保证主线程在一轮所有任务全部计算完成后,再读取new_x的结果:
for(int it=0; it<k; it++){ { lock_guard<mutex> lock(task_mtx); task_count = n; // 标记本轮共n个待执行任务 } for(int i=0; i<n;i++){ auto fx = bind(f, cref(matrix), cref(x), cref(b), ref(new_x), i, n); bind_submit(fx); } // 阻塞等待所有任务执行完成 { unique_lock<mutex> lock(task_mtx); task_cond.wait(lock, [&](){return task_count == 0;}); } // 此时new_x所有位置计算完成,再打印 print_vector(new_x); x.swap(new_x); // 用swap替代直接赋值,性能更高 }
3. 修复其他潜在问题
- 线程工作函数
body中,当stop为true且任务队列为空时,会执行默认初始化的空function<void()>,触发调用异常,需要增加判断:只有当拿到有效任务时才执行t()。 - 因为每个任务只会写入
new_x的独立下标i,不会出现多线程同时写同一个元素的竞争问题,写入new_x时不需要额外加锁。 - 程序退出前记得将
stop设为true唤醒所有工作线程,再join回收线程资源,避免程序异常退出。
内容的提问来源于stack exchange,提问作者JayJona
相关产品推荐
相关产品推荐

