为什么当 std::thread 用于多线程时,特征矩阵计算 return 为零?

Why does Eigen matrix calculations return zero when std::thread is used for multi threading?

我正在尝试实施多线程以将某些 cpu 密集型操作分配给不同的线程。然而,当使用多线程时,任何 Eigen 声明总是 returns 零。以下是演示此问题的示例代码

#include "../Eigen/Core"
#include "../Eigen/LU"
#include "../Eigen/Sparse"
#include "../Eigen/StdVector"
#include <iostream>

#include <thread>
#include <mutex>

using namespace std;
using namespace Eigen;


void Multiply_3x3 (MatrixXd& matrix1, MatrixXd& matrix2, MatrixXd& results);

int main()
{

  MatrixXd a(3, 3);
  MatrixXd b(3, 3);
  MatrixXd c(3,3);

  a<<2,4,3,1,5,7,0,2,3;
  b<<2,4,3,1,5,7,0,2,3;
  
  //Multiply_3x3(a,b,c);
  std::thread thread_multi(Multiply_3x3, a, b, c);
  thread_multi.join();

  cout << " *** In Main Program ***" << endl;
  cout <<c(0,0)<<" , "<< c(0,1)<<" , "<< c(0,1)<<endl;
  cout<<c(1,0)<<" , "<<c(1,1)<<" , "<<c(1,1)<<endl;
  cout<<c(2,0)<<" , "<<c(2,1)<<" , "<<c(2,1)<<endl;


}

void Multiply_3x3 (MatrixXd& matrix1, MatrixXd& matrix2, MatrixXd& results)
{
  results = matrix1 * matrix2;

  cout << " *** In Multiply_3x3 ***" << endl;
  cout<<results(0,0)<<" , "<<results(0,1)<<" , "<<results(0,1)<<endl;
  cout<<results(1,0)<<" , "<<results(1,1)<<" , "<<results(1,1)<<endl;
  cout<<results(2,0)<<" , "<<results(2,1)<<" , "<<results(2,1)<<endl<<endl<<endl;  
}

这个运行的输出是

 *** In Multiply_3x3 ***
8 , 34 , 34
7 , 43 , 43
2 , 16 , 16


 *** In Main Program ***
1.69122e-306 , 1.95819e-306 , 1.95819e-306
9.34612e-307 , 6.23037e-307 , 6.23037e-307
9.34602e-307 , 6.2304e-307 , 6.2304e-307

如输出所示,函数 Multiply_3x3 中的结果是正确的。但是返回值为零。如果我不使用 std:thread 并调用函数,如推荐部分所示 (//Multiply_3x3(a,b,c);) 结果在调用函数中是正确的。想知道是否有人可以指出我在这里遗漏的内容。

在构造 thread 时,您必须将 std::ref 应用于所有矩阵 abc。如果没有这个,std::thread 制作一个副本传递参数并对它们进行操作,更改 c 矩阵的副本而不是 main.

中定义的原始 c 矩阵
  std::thread thread_multi(Multiply_3x3, std::ref(a), std::ref(b), std::ref(c));
  thread_multi.join();

Demo


请注意,如果没有 std::ref,您的代码将无法在 G++ 上编译,看起来您使用的 Microsoft Visual 具有一些扩展(默认启用)——例如,将临时对象绑定到 左值参考。这就是编译您的代码的原因。您可以通过将 /Za 添加到编译器的标志来关闭此功能。

不完全确定你是如何编译和工作的。我无法在 MSVC 上编译它。

但是,请记住 std::thread 的构造函数不关心传入参数的类型。它只是将参数复制到内部存储(可由新创建的线程访问)并且是作为临时值(右值)传递给可调用对象。

例如,

void foo(std::string& s);
std::thread t1(foo, "hello");
std::string arg("hello");
std::thread t2(foo, arg);

将不起作用,因为不可能从临时对象构造非常量引用。

但是

void foo(const std::string& s);
std::thread t1(foo, "hello");
std::string arg("hello");
std::thread t2(foo, arg);

没问题,因为该函数采用 const 引用。

所以你只需要用 std::ref 包装需要引用的参数,这样就不会为函数提供临时副本。然后它将编译并正常工作。