并行搜索不同的值?

时间:2014-06-21 22:53:59

标签: c++ c++11 concurrency parallel-processing mutex

请考虑以下代码:

// Preprocessor
#include <iostream>
#include <chrono>
#include <thread>
#include <algorithm>
#include <mutex>
#include <random>

// Main function
int main()
{
    // A random vector of size 100 with 10 different random values
    std::vector<unsigned int> vector = make_random_vector(100, 10);
    // At the end, the result should be the 10 different random values
    std::vector<unsigned int> result;
    // Mutex to deals with concurrency
    std::mutex mutex;
    // Parallel search
    parallel_for_each(vector.begin(), vector.end(), 
    [=, &result, &mutex](const unsigned int& i){
       /* CRITICAL SECTION: BEGIN */
       // If the current element is not yet in the resulting vector, inserts it
       if (!std::binary_search(result.begin(), result.end(), i)) {
           mutex.lock();
           result.insert(std::lower_bound(result.begin(), result.end(), i), i);
           mutex.unlock();
       }
       /* CRITICAL SECTION: END */
    });
    // Unique values
    result.erase(std::unique(result.begin(), result.end()), result.end());
    // Display the result
    std::for_each(result.begin(), result.end(), 
    [](const unsigned int& i){
        std::cout<<i<<std::endl;
    });
    // Finalization
    return 0;
}

目标是并行地在矢量中找到n个不同的值。

我的问题是:前面的代码是OK(没有并发问题),如果没有,如何纠正呢?


注意:此代码调用了两个函数:

parallel_for_each在提供的线程数上执行提供的函数:

// Parallel execution returning the execution time in seconds
template <class Iterator, class Function> 
double parallel_for_each(const Iterator& first, const Iterator& last, Function&& function, const int nthreads = std::thread::hardware_concurrency())
{
    const std::chrono::high_resolution_clock::time_point tbegin = std::chrono::high_resolution_clock::now();
    const long long int ntasks = std::max(static_cast<int>(1), nthreads);
    const long long int group = std::max(static_cast<long long int>(first < last), static_cast<long long int>((last-first)/ntasks));
    std::vector<std::thread> threads;
    Iterator it = first;
    threads.reserve(ntasks);
    for (it = first; it < last-group; it += group) {
        threads.push_back(std::thread([=, &last, &group, &function](){std::for_each(it, std::min(it+group, last), function);}));
    }
    std::for_each(it, last, function);
    std::for_each(threads.begin(), threads.end(), [](std::thread& current){current.join();});
    return std::chrono::duration_cast<std::chrono::duration<double> >(std::chrono::high_resolution_clock::now()-tbegin).count();
}

make_random_vector,它产生一个随机的nelements向量,nvalues具有不同的随机值

// Produces a random vector of nelements with nvalues different random values
std::vector<unsigned int> make_random_vector(const unsigned int nelements, const unsigned int nvalues)
{
    std::vector<unsigned int> vector(nelements);
    std::vector<unsigned int> values(nvalues);
    std::random_device device;
    std::mt19937 engine(device());
    std::uniform_int_distribution<unsigned int> distribution1;
    std::uniform_int_distribution<unsigned int> distribution2(0, nvalues-1);
    std::for_each(values.begin(), values.end(), [=, &distribution1, &engine](unsigned int& i){i = distribution1(engine);});
    std::for_each(vector.begin(), vector.end(), [=, &distribution2, &engine, &values](unsigned int& i){i = values[distribution2(engine)];});
    return vector;
}

2 个答案:

答案 0 :(得分:1)

您的代码有问题,因为您只保护并发写访问权,但不保护result的读访问权。

解决方案是将互斥锁定移到if之外,如下所示:

[=, &result, &mutex](const unsigned int& i){
    std::lock_guard<std::mutex> lck (mutex);

    // If the current element is not yet in the resulting vector, inserts it
    if (!std::binary_search(result.begin(), result.end(), i)) {
        result.insert(std::lower_bound(result.begin(), result.end(), i), i);
    }
}

但它会打破并行的目的:/

另一种解决方案是处理不同的结果集,并在循环结束时加入结果。

其他解决方案可能是Double-checked locking的变体,但需要在每次插入时复制result

答案 1 :(得分:0)

而不是使用std::vector<unsigned int>使用Concurrency::combinable<std::vector<unsigned int>> result。这允许拥有结果的线程本地副本,而您不需要任何互斥锁。

使用parallel_for_each使用combine_each完成后,将结果放入std::set<unsigned int>以获取唯一值或您认为合适的任何方式。

编辑:以下方法不需要std :: mutex。

 #include <ppl.h>

 void print_unqiue_numbers()
 {
    using namespace Concurrency;
    std::vector<unsigned int> vector = make_random_vector(100, 10);
    // At the end, the result should be the 10 different random values
    combinable<std::vector<unsigned int>> result;
    // Parallel search
    parallel_for_each(vector.begin(), vector.end(), 
    [=, &result](const unsigned int& i){
       auto& local_result = result.local(); // thread local variable.
       if (!std::binary_search(local_result.begin(), local_result.end(), i)) {
           local_result.insert(std::lower_bound(local_result.begin(),
                                                local_result.end(), i), i);
       }
    });

     std::set<unsigned int> unique_values;
     result.combine_each([&](std::vector<unsigned int> const& values)
     {
        for(auto v : values)
        {
            unique_values.insert(v);
        }
     });

     std::cout << "print the unique values\n";
     for (auto v : unique_values)
     {
         std::cout << v << '\n';
     }
  }