【问题标题】:What's the difference between functor and lambda when used for constructing std::thread用于构造 std::thread 时,仿函数和 lambda 有什么区别
【发布时间】:2014-11-17 11:23:24
【问题描述】:

我正在测试C++ Concurrency in Action 一书中的代码。基本上,它使用多线程来实现与std::accumulate相同的功能。如下所示,我尝试使用 lambda 代替仿函数,但我的代码给出了错误的结果。 可以切换变量is_from_book 来测试这两种方式。

#include <thread>
#include <algorithm>
#include <vector>
#include <iostream>

namespace para {

template<typename Iter, typename Value>
struct AccumulateBlock
{
    void operator ()(Iter first, Iter last, Value& result)
    {
        result = std::accumulate(first, last, result);
    }
};

template<typename Iter, typename Value>
Value parallel_accumulate(Iter first, Iter last, Value init_val)
{
    using std::size_t;
    size_t length = std::distance(first, last);
    if(length == 0) return init_val;    // trivial case

    size_t min_per_thread = 25;
    size_t max_threads = (length + min_per_thread - 1) / min_per_thread;
    size_t hardware_threads = std::thread::hardware_concurrency();
    size_t num_threads = std::min((hardware_threads!=0 ? hardware_threads : 2), max_threads);
    size_t block_size = length/num_threads;
    std::vector<Value> results(num_threads);
    std::vector<std::thread> threads{num_threads - 1};

    Iter block_start = first;
    for(unsigned long idx=0; idx!=(num_threads-1);  ++idx )
    {
        Iter block_end = block_start;
        std::advance(block_end, block_size);

        if(bool is_from_book = false)   //code from the book that uses functor
        {
            threads[idx] = std::thread{
                    para::AccumulateBlock<Iter, Value>{},
                    block_start,
                    block_end,
                    std::ref(results[idx])
            };
        }
        else    //my code that tries to use lambda instead of functor
        {
            threads[idx] = std::thread{
                [&]{
                    results[idx] = std::accumulate(block_start, block_end, results[idx]);
                }
            };
        }

        block_start = block_end;
    }

    para::AccumulateBlock<Iter, Value>{}(block_start, last, results[num_threads-1]);

    for(auto& t : threads)  t.join();
    return std::accumulate(results.begin(), results.end(), init_val);
}
}//namespace

int main()
{
    std::vector<int> v(10000,1);
    auto sum = para::parallel_accumulate(v.begin(), v.end(), 0);
    std::cout << "sum = " << sum << std::endl;

    return 0;
}

我的问题是什么问题?我做得对吗?这两种方式有什么区别吗?如何解决?谢谢。

【问题讨论】:

  • 当你说你的代码“不工作”时,你的意思是什么?你有构建错误(那是什么)?您是否有运行时错误(例如崩溃)?结果是不是你所期望的(did 你所期望的,实际结果是什么)?请详细说明。
  • std::thread 的使用看起来不安全?
  • @JoachimPileborg 我期待10000,但我的代码给出了错误的结果..而且每次给出不同的结果。
  • @paulm:这取决于您所说的“安全”是什么意思。此处错误点的异常将终止程序,这可能是也可能不是您想要的,但肯定比 UB 更好。
  • 我的意思是 std::thread 将在 RAII 包装器中,否则异常意味着它们不会加入()?

标签: c++ multithreading c++11 lambda


【解决方案1】:

您通过引用捕获所有内容,因此 idxblock_startblock_end 在线程脚下移动,导致各种未定义的行为。

通过引用捕获results - 或者,更安全的是,只捕获线程需要的数组元素 - 并通过值捕获其他元素:

Value & result = results[idx];
threads[idx] = std::thread{
    [&result,block_start,block_end]{   // or [=,&result] if you like brevity
        result = std::accumulate(block_start, block_end, result);
    }
};

【讨论】:

  • 现在工作。我也试过[&amp;result,=],没用,但是[=, &amp;result]works.Thx man~
  • @YueWang:没错,我忘记了捕获默认值必须放在第一位。现已更正。
猜你喜欢
  • 2016-03-24
  • 1970-01-01
  • 2012-01-22
  • 2019-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
相关资源
最近更新 更多