请考虑以下代码:
// 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;
}
答案 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';
}
}