如何将模板函数传递给同一 .cpp 文件中的线程?
How do I pass a template function to a thread within the same .cpp file?
我有一个任务要实现并行版本的最长公共子序列算法(只计算 LCS 长度)。程序必须使用线程,以便尽可能快地完成任务(至少,比顺序执行更快)。理想情况下,它还应该在线程中使用 TLS。我们有一个类似的作业,它在 .hpp 文件中实现了一个模板,我想使用相同的模板,但我似乎无法在此作业中使用 .hpp 文件。我的问题在于将模板函数传递给我的线程。下面是代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <chrono>
#include <thread>
#include <functional>
#ifdef __cplusplus
extern "C" {
#endif
void generateLCS(char* X, int m, char* Y, int n);
void checkLCS(char* X, int m, char* Y, int n, int result);
#ifdef __cplusplus
}
#endif
class ParFor {
public:
template<typename TLS>
void parfor(size_t beg, size_t endm, size_t endn, size_t increment,
std::function<void(TLS&)> before,
std::function<void(int, int, TLS&)> f,
std::function<void(TLS&)> after
) {
TLS tls;
before(tls);
for (size_t a=beg; a<endm; a+= increment) {
for (size_t b=beg; b<endn; b+= increment) {
f(a, b, tls);
}
}
after(tls);
}
};
int main (int argc, char* argv[]) {
if (argc < 4) { std::cerr<<"usage: "<<argv[0]<<" <m> <n> <nbthreads>"<<std::endl;
return -1;
}
int m = atoi(argv[1]);
int n = atoi(argv[2]);
int nbthreads = atoi(argv[3]);
// get string data
char *X = new char[m];
char *Y = new char[n];
generateLCS(X, m, Y, n);
int result = 0; // length of common subsequence
std::vector<std::thread> threads (nbthreads);
int mSubset = m / nbthreads;
int nSubset = n / nbthreads;
ParFor pf;
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();
for(int j = 0; j < nbthreads; j++) {
threads.push_back(std::thread(&ParFor::parfor, &pf, 0, mSubset, nSubset, 1,
[&](int& tls) -> void{
tls = result;
},
[&](int a, int b, int& tls) -> void{
if (X[a] == Y[b])
tls++;
},
[&](int tls) -> void{
result += tls;
}));
}
for(auto& t: threads)
t.join();
std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
if(m < 10 || n < 10)
result = 0;
checkLCS(X, m, Y, n, result);
std::cerr<<elapsed_seconds.count()<<std::endl;
delete[] X;
delete[] Y;
return 0;
}
“class ParFor”和“int result”到最后的所有内容都是我添加的,其余是TA预先编写的代码。如果需要更多说明,请告诉我。谢谢。
你是对的,你不能像传递函数一样传递函数模板。它们是不同的东西,就像饼干刀不是饼干一样。
你的代码有两个主要问题。
首先,由于 ParFor::parfor 是一个模板,如果您提供的模板参数与您的 lambda 用于 TLS 的参数相匹配,那么您只能将成员函数指针指向它,因此将其更改为此(例如) :
&ParFor::parfor<int>
你的第二个问题是试图将 lambda 传递给函数模板,并期望推论说“这个 lambda 可以转换为 std::function,所以它是一个匹配项,从中推导出来”。对于推导,它需要精确匹配类型,lambda 不是 std::function。如果你传入std::function个对象,那么它可以推导出模板参数。
因此,更改创建线程的循环以在传递它们之前包装 lambda,这样它就会编译。您的代码中发生的事情太多了,我不想深入了解除此之外的细节,所以如果还有其他错误,那些仍然是您的。 :)
for(int j = 0; j < nbthreads; j++) {
threads.push_back(std::thread(
&ParFor::parfor<int>, &pf, 0, mSubset, nSubset, 1,
std::function{[&](int& tls) -> void{
tls = result;
}},
std::function{[&](int a, int b, int& tls) -> void {
if (X[a] == Y[b])
tls++;
}},
std::function{[&](int tls) -> void{
result += tls;
}}));
}
此外,指定 lambdas return void 的尾部 return 类型是多余的。您可以简单地删除 -> void
而无需更改任何内容。
我有一个任务要实现并行版本的最长公共子序列算法(只计算 LCS 长度)。程序必须使用线程,以便尽可能快地完成任务(至少,比顺序执行更快)。理想情况下,它还应该在线程中使用 TLS。我们有一个类似的作业,它在 .hpp 文件中实现了一个模板,我想使用相同的模板,但我似乎无法在此作业中使用 .hpp 文件。我的问题在于将模板函数传递给我的线程。下面是代码:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <chrono>
#include <thread>
#include <functional>
#ifdef __cplusplus
extern "C" {
#endif
void generateLCS(char* X, int m, char* Y, int n);
void checkLCS(char* X, int m, char* Y, int n, int result);
#ifdef __cplusplus
}
#endif
class ParFor {
public:
template<typename TLS>
void parfor(size_t beg, size_t endm, size_t endn, size_t increment,
std::function<void(TLS&)> before,
std::function<void(int, int, TLS&)> f,
std::function<void(TLS&)> after
) {
TLS tls;
before(tls);
for (size_t a=beg; a<endm; a+= increment) {
for (size_t b=beg; b<endn; b+= increment) {
f(a, b, tls);
}
}
after(tls);
}
};
int main (int argc, char* argv[]) {
if (argc < 4) { std::cerr<<"usage: "<<argv[0]<<" <m> <n> <nbthreads>"<<std::endl;
return -1;
}
int m = atoi(argv[1]);
int n = atoi(argv[2]);
int nbthreads = atoi(argv[3]);
// get string data
char *X = new char[m];
char *Y = new char[n];
generateLCS(X, m, Y, n);
int result = 0; // length of common subsequence
std::vector<std::thread> threads (nbthreads);
int mSubset = m / nbthreads;
int nSubset = n / nbthreads;
ParFor pf;
std::chrono::time_point<std::chrono::system_clock> start = std::chrono::system_clock::now();
for(int j = 0; j < nbthreads; j++) {
threads.push_back(std::thread(&ParFor::parfor, &pf, 0, mSubset, nSubset, 1,
[&](int& tls) -> void{
tls = result;
},
[&](int a, int b, int& tls) -> void{
if (X[a] == Y[b])
tls++;
},
[&](int tls) -> void{
result += tls;
}));
}
for(auto& t: threads)
t.join();
std::chrono::time_point<std::chrono::system_clock> end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
if(m < 10 || n < 10)
result = 0;
checkLCS(X, m, Y, n, result);
std::cerr<<elapsed_seconds.count()<<std::endl;
delete[] X;
delete[] Y;
return 0;
}
“class ParFor”和“int result”到最后的所有内容都是我添加的,其余是TA预先编写的代码。如果需要更多说明,请告诉我。谢谢。
你是对的,你不能像传递函数一样传递函数模板。它们是不同的东西,就像饼干刀不是饼干一样。
你的代码有两个主要问题。
首先,由于 ParFor::parfor 是一个模板,如果您提供的模板参数与您的 lambda 用于 TLS 的参数相匹配,那么您只能将成员函数指针指向它,因此将其更改为此(例如) :
&ParFor::parfor<int>
你的第二个问题是试图将 lambda 传递给函数模板,并期望推论说“这个 lambda 可以转换为 std::function,所以它是一个匹配项,从中推导出来”。对于推导,它需要精确匹配类型,lambda 不是 std::function。如果你传入std::function个对象,那么它可以推导出模板参数。
因此,更改创建线程的循环以在传递它们之前包装 lambda,这样它就会编译。您的代码中发生的事情太多了,我不想深入了解除此之外的细节,所以如果还有其他错误,那些仍然是您的。 :)
for(int j = 0; j < nbthreads; j++) {
threads.push_back(std::thread(
&ParFor::parfor<int>, &pf, 0, mSubset, nSubset, 1,
std::function{[&](int& tls) -> void{
tls = result;
}},
std::function{[&](int a, int b, int& tls) -> void {
if (X[a] == Y[b])
tls++;
}},
std::function{[&](int tls) -> void{
result += tls;
}}));
}
此外,指定 lambdas return void 的尾部 return 类型是多余的。您可以简单地删除 -> void
而无需更改任何内容。