Вот мой класс атомного счетчика, используемый для кода TBB.
#include <atomic>
template <typename T = int>
struct AtomicCounter {
private:
std::atomic<T> value;
std::atomic<T> num_inter;
public:
void put_inter(T niter) {
T x = num_inter.fetch_add(niter);
}
T get_inter() { return num_inter.load(); }
void increment() { ++value; }
void put(T data) { value.fetch_add(data) ; // ignore the old value returned }
void decrement() { --value; }
T get() { return value.load(); }
};
Я использую параллельный цикл foreach, в котором каждый поток имеет свое собственное значение локального счетчика, и он обновляет объект глобального счетчика, используя функцию put класса AtomicCounter. Объект функции для параллельного foreach принимает этот глобальный счетчик в качестве ссылки.
template<typename counter_t>
struct my_functor {
public:
my_functor(){}
my_functor(counter_t &c):m_c(c){ }
template<typename label_t>
void operator()(label_t label) const {
// do some work --> local counter
m_c.put(local_value); // put the local counter value in the global counter
}
private:
counter_t& m_c;
mutable int local_value; //local counter value
}
// for TBB
//declare an object of this class as a global counter.
AtmoicCounter<int> c;
my_functor<atomicCounter<int>> func(c) //functor object
parallel_for_each( mywork.begin(), mywork.end(), func) //TBB parallel for each
В общем, каждый поток обновляет глобальный счетчик. Что-то не так с классом atomicCounter, где я объявляю член std :: atomic? Я использую GCC 4.7.2 с функциями C ++ 11 и Intel TBB 4.2
Спасибо!
Я не вижу никаких проблем в декларациях. Если вы по какой-то причине не уверены в атомарности GCC, используйте атомарность TBB, в которой я уверен.
Но где я вижу, проблема заключается в использовании mutable
ключевое слово для доступа к полям из const
функтор. mutable
часто является признаком плохого дизайна. И в параллельном программировании его использование особенно подвержено ошибкам. Если метод, который вызывается параллельно, помечается как const, это часто означает, что он будет вызван параллельно в том же экземпляре класса функтора. Таким образом, у вас есть гонка данных на изменчивом поле.
Вместо того, чтобы нарушать ограничения const, вы можете переместить local_value в область действия operator()
или попробуйте локальные контейнеры tbb::combinable
или же tbb::enumerable_thread_specific
кэшировать частичные значения, специфичные для потока, или применять алгоритм параллельного сокращения tbb::parallel_reduce
где вы можете безопасно хранить частичные результаты в теле функтора.