我正在尝试生成将执行给定任务的多个线程。此任务根据传入的内容而有所不同,并将返回多个值。
我已经尝试过以下几行,没有运气:
std::vector<std::tuple<std::thread, Task, Result1, Result2>> workers;
for (auto const& task : tasks) {
Result1 result1;
Result2 result2;
std::tuple<std::thread, Task, Result1, Result2> worker = std::make_tuple(std::thread(&Slave::performTask, this, task, std::ref(result1), std::ref(result2)), task, result1, result2);
workers.emplace_back(worker);
}
for (auto& w : workers) {
std::get<0>(w).join();
std::cout << "Task=" << std::get<1>(w) << " Result1=" << std::get<2>(w) << " Result2=" << std::get<3>(w) << std::endl;
}
我认为问题在于在嵌套容器中引用std :: thread会导致workers.emplace_back(worker);
无效。我尝试在几个地方使用std :: move但没有成功。
workers.emplace_back(worker);
会导致以下错误:
external / libcxx / include / thread:268:5:错误:&#39; std :: __ 1 :: thread :: thread(const std :: __ 1 :: thread&amp;)&#39;是私人的 thread(const thread&amp;);
external / libcxx / include / type_traits:887:87:错误:在此上下文中 sizeof(__ is_convertible_imp :: __ test&lt; _T2&gt;(__ is_convertible_imp :: __ source&lt; _T1&gt;()))== 1
external / libcxx / include / type_traits:851:28:错误:初始化参数1&#39; char std :: __ 1 :: __ is_convertible_imp :: __ test(_Tp)[with _Tp = std :: __ 1 :: thread ]&#39; template char __test(_Tp);
根据Daniel的要求,我已经整理了一个最小的可编译示例。它在cpp.sh上编译并正确执行:
#include <iostream>
#include <thread>
#include <tuple>
#include <vector>
using Task = std::string;
using Result1 = std::string;
using Result2 = std::string;
class Slave {
private:
void performTask(Task task, Result1& result1, Result2& result2) {
if (task == "A") {
result1 = "A1";
result2 = "A2";
} else if (task == "B") {
result1 = "B1";
result2 = "B2";
} else {
result1 = "C1";
result2 = "C2";
}
}
public:
std::vector<Task> tasks;
std::vector<std::tuple<Task, Result1, Result2>> performParallel() {
std::vector<std::tuple<Task, Result1, Result2>> results;
std::vector<std::tuple<std::thread, Task, std::unique_ptr<Result1>, std::unique_ptr<Result2>>> workers;
for (auto const& task : tasks) {
std::unique_ptr<Result1> result1(new Result1);
std::unique_ptr<Result2> result2(new Result2);
std::tuple<std::thread, Task, std::unique_ptr<Result1>, std::unique_ptr<Result2>> worker = std::make_tuple(std::move(std::thread(&Slave::performTask, this, task, std::ref(*result1), std::ref(*result2))), task, std::move(result1), std::move(result2));
workers.emplace_back(std::move(worker));
}
for (auto& w : workers) {
std::get<0>(w).join();
std::tuple<Task, Result1, Result2> result = std::make_tuple(std::get<1>(w), *std::get<2>(w), *std::get<3>(w));
results.emplace_back(result);
}
return results;
}
};
int main() {
Slave slave;
slave.tasks = { "A", "B", "C" };
std::vector<std::tuple<Task, Result1, Result2>> results = slave.performParallel();
for (auto const& r : results) {
std::cout << "Task=" << std::get<0>(r) << " Result1=" << std::get<1>(r) << " Result2=" << std::get<2>(r) << std::endl;
}
}
执行输出:
任务= A Result1 = A1 Result2 = A2
任务= B结果1 = B1结果2 = B2
任务= C结果1 = C1结果2 = C2
答案 0 :(得分:0)
线程不是 copy constructible ,因此错误'thread(const thread&)' is private
线程 移动可构造,因此您可以使用emplace_back
std::move
进行右值引用:
workers.emplace_back(std::move(worker));
虽然这可以解决眼前的问题,但请注意你有一个更大,更险恶的问题 - 即悬空参考
您正在将对temporaries的引用传递给您的线程函数(然后创建一个包含Result1
和Result2
的副本的元组<) / p>
Result1 result1;
Result2 result2;
std::tuple<std::thread, Task, Result1, Result2> worker =
std::make_tuple(
std::thread(
&Slave::performTask,
this,
task,
std::ref(result1), <-- here you pass a reference to a local variable
std::ref(result2)), <--
task,
result1, <-- here you copy construct the tuple member from a local var
result2); <--
您的主题将引用对result1
和result2
的悬空引用,并且可能会出现seg-fault。
您可以首先使用空线程对象创建元组,然后创建传递对元组成员的引用的线程,但我不建议这样做。
更好的设计是使用一些封装,将你的线程和结果变量打包在他们自己的类中
struct Worker
{
Worker(Task& task)
: task(task)
, _thread(&Worker::performTask, this)
{
}
void performTask()
{
result1 = task.foo();
result2 = task.bar();
}
void join()
{
_thread.join();
}
Task task;
Result1 result1;
Result2 result2;
private:
std::thread _thread;
};
现在您不必尝试使用Result
变量和传递对线程元组成员的引用来创建元组。