我们有一个模板方法,它使用线程池,根据可用的系统线程数,一般并行地执行某些成员函数。
该方法必须使用的两个参数是固定的,第一个是线程索引,第二个是线程数。
这样,这些方法就可以执行如下操作:
for(i = threadIndex; i < workContainer.size(); i += numThreads)
{
// So this thread will only do every numThreads piece of the work from the workContainer, with no overlap between threads
}这是一个很好的方法,下面是这个方法的样子:
template <class S, class Args>
void parallelWork(S* self, void (S::*workfcn)(const unsigned, const unsigned, Args*), Args* args)
{
auto work = [&](const unsigned threadIndex, const unsigned nthreads, S* self, Args* args)
{
std::string* rc = nullptr;
try
{
(self->*workfcn)(threadIndex, nthreads, args);
}
catch (std::exception& err)
{
rc = new std::string(err.what());
}
return rc;
};
ulong nthreads = size();
std::vector<std::string*> rc(nthreads);
if (1 == nthreads)
{
rc[0] = work(0, 1, self, args);
}
else
{
std::vector<std::future<std::string*>> frc(nthreads);
for (unsigned i = 0; nthreads > i; ++i)
frc[i] = enqueue(work, i, nthreads, self, args);
// Wait for all
for (unsigned i = 0; nthreads > i; ++i)
rc[i] = frc[i].get();
}
for (unsigned i = 0; nthreads > i; ++i)
{
if (rc[i])
{
HELAS_ERROR(std::string("Thread ") + std::to_string(i) + ": " + *rc[i]); delete rc[i];
}
}
}然而,这是有限的。当前解决方案要求我们有效地将所有参数(其中一些是引用)放入结构中,然后将指向该结构的指针传递到此parallelWork()方法。
如果我们可以直接传递方法应该采用的参数,那不是很好吗?IE,不是这样做的:
struct ThreadArgs
{
const std::vector<int>& inArg1;
double inArg2;
SomeClass* inarg3;
std::vector<std::vector<double>> outArg1; // outer vector is per-thread
}void MyClass::doFooInParallel()
{
std::vector<int> inArg1;
double inArg2;
SomeClass* cp = getSomeClass();
std::vector<std::vector<double>> outArg1(numThreads);
ThreadArgs args = ThreadArgs(inArg1, inArg2, cp, outArg1);
parallelWork(this, &MyClass::foo, &args);
}我们可以这样做:
void MyClass::doFooInParallel()
{
std::vector<int> inArg1;
double inArg2;
SomeClass* cp = getSomeClass();
std::vector<std::vector<double>> outArg1(numThreads);
parallelWork(this, &MyClass::foo, inArg1, inArg2, cp, outArg1);
}它似乎是一个不同的模板将是解决方案,将方法的开始改为:
template <class S, class... Args>
void parallelWork(S* self,void (S::*workfcn)(const unsigned, const unsigned, Args... args),Args... args)
{
auto work = [&](const unsigned threadIndex, const unsigned nthreads,S* self, Args... args)这确实与我们当前使用的方法进行了编译。然而,存在一个问题--这些论点是按值传递的,而不是按引用传递的.
对于输入,这可能并不坏,有些可能通过复制省略而消除。但是,对于传递的变量,即方法的输出(就像向量n线程大小与每个线程的输出一样),这显然是不起作用的--它意味着并行地执行工作,而实际上什么也不做。
我试过像这样引用他们:
template <class S, class... Args>
void parallelWork(S* self,void (S::*workfcn)(const unsigned, const unsigned, Args&&... args),Args&&... args)
{
auto work = [&](const unsigned threadIndex, const unsigned nthreads,S* self, Args&&... args)然而,当我们尝试使用它时,这是失败的。
could not match type-parameter 0-1&& against (the actual parameter we tried to pass)
(单次引用也失败,但我测试了以防万一)
解决这一问题的办法是什么?这在C++11中是可能的,还是只适用于后来C++标准中的特性?
std::forward、std::ref或std::cref在这里会有所帮助吗?
这实际上是一个概念性的问题,所以请原谅我,所有这些代码都不能运行,要想得到所有这些的可运行的示例,这将是一项巨大的工作,我认为这个问题已经得到了很好的解释,足以涵盖它,但是如果需要澄清,请告诉我。
发布于 2020-07-31 16:06:09
&&不是引用,而是r值(可以是引用,也可以是值)。您应该使用Args& ...。我认为问题在于您使用的是template <class S, class... Args>,您应该在其中使用template <class S, typename... Args>,因为您的参数似乎并不总是类。
如果您想使用转发(例如,std::vector.emplace_back ),可以使用Args && ...,然后对函数使用std::forward参数。在您的例子中,您应该注意到,使用std::forward仍然会通过值而不是引用传递您的参数。要实现通过r值传递参数,请使用std::move!
https://stackoverflow.com/questions/63195090
复制相似问题