Простой пример программы, которая использует мьютекс tbb::queuing внутри tbb::parallel_for, не компилируется
Вот игрушечный пример, с которым я играю, чтобы узнать, как использовать TBB. Parallel::operator() должен работать параллельно, но он имеет критическую область, к которой должен обращаться только один процессор за раз, поэтому сообщение, которое он печатает, не шифруется. Моя проблема в том, что он не компилируется, и сообщение компилятора мне не особо помогает. Что я делаю неправильно?
Кроме того, считается ли это правильным способом реализации мьютекса внутри parallel_for?
#include <iostream>
#include <vector>
#include <cmath>
#include <tbb/tbb.h>
typedef tbb::queuing_mutex Mutex;
struct Parallel
{
Mutex mutex;
std::vector<int> * values;
Parallel(std::vector<int> * values_) : values(values_) {}
void operator()( tbb::blocked_range< unsigned int > & range ) const {
for(unsigned int i = range.begin(); i < range.end(); ++i) {
{
Mutex::scoped_lock lock(mutex);
if ( (*values)[i] > 40)
{
std::cout << "NO SCRAMBLING ALLOWED!\n";
std::cout.flush();
}
lock.release();
}
}
}
};
int main() {
const int someValue = 20000;
std::vector<int> data(someValue);
for(int i = 0; i < someValue; ++i) {
data[i] = std::rand();
}
tbb::parallel_for( tbb::blocked_range<unsigned int>(0, data.size()),
Parallel(&data) );
}
Ниже приведено сообщение об ошибке:
/path-to-src/main.cpp: In member function 'void Parallel::operator()(tbb::blocked_range<unsigned int>&) const':
/path-to-src/main.cpp:20:46: error: no matching function for call to 'tbb::queuing_mutex::scoped_lock::scoped_lock(const Mutex&)'
/path-to-src/main.cpp:20:46: note: candidates are:
In file included from /usr/include/tbb/tbb.h:65:0,
from /path-to-src/main.cpp:4:
/usr/include/tbb/queuing_mutex.h:80:9: note: tbb::queuing_mutex::scoped_lock::scoped_lock(tbb::queuing_mutex&)
/usr/include/tbb/queuing_mutex.h:80:9: note: no known conversion for argument 1 from 'const Mutex {aka const tbb::queuing_mutex}' to 'tbb::queuing_mutex&'
/usr/include/tbb/queuing_mutex.h:77:9: note: tbb::queuing_mutex::scoped_lock::scoped_lock()
/usr/include/tbb/queuing_mutex.h:77:9: note: candidate expects 0 arguments, 1 provided
/usr/include/tbb/queuing_mutex.h:66:11: note: tbb::queuing_mutex::scoped_lock::scoped_lock(const tbb::queuing_mutex::scoped_lock&)
/usr/include/tbb/queuing_mutex.h:66:11: note: no known conversion for argument 1 from 'const Mutex {aka const tbb::queuing_mutex}' to 'const tbb::queuing_mutex::scoped_lock&'
2 ответа
tbb::parallel_for был разработан, чтобы не компилировать пример как написано. Это защищает от ошибки в коде. Форма диапазона tbb::rallel_for копирует функтор по значению в несколько объектов задачи. Следовательно, у каждой задачи будет отдельная копия мьютекса, и, таким образом, мьютекс не обеспечит необходимую синхронизацию.
Способ исправить код - объявить мьютекс вне структуры Parallel и передать его через указатель, аналогичный указателю для "значений".
Все правильно, кроме того что mutex
должно быть mutable
для того, чтобы его можно было использовать в замке. Более того, снятие блокировки не требуется.
typedef tbb::queuing_mutex Mutex;
struct Parallel
{
mutable Mutex mutex; // mutable so that we can use it in const member
std::vector<int> * values;
Parallel(std::vector<int> * values_) : values(values_) {}
// note: this is a const member
void operator()( tbb::blocked_range< unsigned int > & range ) const
{
for(unsigned int i = range.begin(); i < range.end(); ++i)
{
Mutex::scoped_lock lock(mutex); // requires non-const argument
if ( (*values)[i] > 40)
{
std::cout << "NO SCRAMBLING ALLOWED!\n";
std::cout.flush();
}
// no need to release the lock: the destructor will do that for you
}
}
};
Кроме того, вы можете, конечно, сделать функцию-член неконстантной.