为什么当 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
应用于所有矩阵 a
、b
和 c
。如果没有这个,std::thread
制作一个副本传递参数并对它们进行操作,更改 c
矩阵的副本而不是 main
.
中定义的原始 c
矩阵
std::thread thread_multi(Multiply_3x3, std::ref(a), std::ref(b), std::ref(c));
thread_multi.join();
请注意,如果没有 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
包装需要引用的参数,这样就不会为函数提供临时副本。然后它将编译并正常工作。
我正在尝试实施多线程以将某些 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
应用于所有矩阵 a
、b
和 c
。如果没有这个,std::thread
制作一个副本传递参数并对它们进行操作,更改 c
矩阵的副本而不是 main
.
c
矩阵
std::thread thread_multi(Multiply_3x3, std::ref(a), std::ref(b), std::ref(c));
thread_multi.join();
请注意,如果没有 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
包装需要引用的参数,这样就不会为函数提供临时副本。然后它将编译并正常工作。