产生多个线程,每个线程返回多个值

时间:2016-09-15 19:46:24

标签: c++ multithreading c++11 stdthread

我正在尝试生成将执行给定任务的多个线程。此任务根据传入的内容而有所不同,并将返回多个值。

我已经尝试过以下几行,没有运气:

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

1 个答案:

答案 0 :(得分:0)

线程不是 copy constructible ,因此错误'thread(const thread&)' is private

线程 移动可构造,因此您可以使用emplace_back std::move进行右值引用:

workers.emplace_back(std::move(worker));

虽然这可以解决眼前的问题,但请注意你有一个更大,更险恶的问题 - 即悬空参考

您正在将对temporaries的引用传递给您的线程函数(然后创建一个包含Result1Result2副本的元组<) / 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);  <--

您的主题将引用对result1result2的悬空引用,并且可能会出现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变量传递对线程元组成员的引用来创建元组。