В чем разница между функтором и лямбда при использовании для создания std :: thread

0

Я тестирую код из книги C++ Параллельность в действии. В принципе, он использует многопоточность для реализации той же функции, что и std::accumulate. Как показано ниже, я попытался использовать лямбда вместо функтора, но мой код дал неправильные результаты. Переменная 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;
}

Мой вопрос в чем проблема? Правильно ли я поступаю? Любая разница между двумя способами? Как это исправить? спасибо.

  • 1
    Когда вы говорите, что ваш код «не работает», что вы подразумеваете под этим? У вас есть ошибки сборки (что они тогда)? Есть ли у вас ошибки во время выполнения (например, сбой)? Является ли результат не то , что вы ожидаете (что вы ожидали, и что фактический результат)? Пожалуйста, дополните.
  • 0
    Это использование std :: thread не выглядит безопасным для исключений?
Показать ещё 5 комментариев
Теги:
multithreading
c++11
lambda

1 ответ

5
Лучший ответ

Вы захватываете все по ссылке, поэтому idx, block_start и block_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);
    }
};
  • 1
    Это [=, &result] .
  • 0
    @TC: Так и есть, спасибо.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню