Многопоточное атомарное хранилище / загрузка нескольких значений в C ++

Предположим, у меня есть структура и класс на C ++:

struct Vec {
   double x;
   double y;
   double z;
}

class VecTracker {
   Vec latest_vec;
   std::atomic<double> highest_x;
   std::atomic<double> highest_y;
   std::atomic<double> highest_z;

   //updates highest_x, highest_y, highest_z atomically
   void push_vec(const Vec& v);
   double get_high_x() const;
   double get_high_y() const;
   double get_high_z() const;
   //returns Vec consisting of snapshot of highest_x, highest_y, highest_z
   Vec get_highs() const;
}

У меня будут потоки чтения R и один поток писателя. Поток записи обновит ноль или более участников highest_*. Если поток чтения вызывает get_highs(), мне нужно, чтобы все записи из текущего вызова функции push_vec() потока записи были видны потоку чтения до, поток чтения читает highest_x, highest_y и т. Д. Для создания вектора .

Теперь я знаю, что если Vec достаточно мало, я мог бы просто использовать std::atomic<Vec>. Проблема в том, что если он слишком большой, инструкции собственного процессора для этого хранилища / загрузки не могут быть использованы. Есть ли способ использовать std::atomic_thread_fence, чтобы гарантировать, что поток записи совершает несколько атомарных операций записи до того, как поток чтения заберет их? То есть гарантия того, что все записи потока записи будут зафиксированы до того, как поток чтения увидит какую-либо из них? Или std::atomic_thread_fence только обеспечивает гарантии переупорядочения внутри потока? В настоящее время простое использование .store(std::memory_order_release) для каждого члена, похоже, не гарантирует, что все три сохранения произойдут до любого чтения.

Очевидно, я мог бы использовать здесь блокировку, но в идеале я хочу найти способ сделать эту структуру данных свободной от блокировок.

Я знаю, что могу поместить highest_x, highest_y и highest_z в одну структуру и разместить две ее копии в куче, атомарно меняя местами указатели после каждой записи. Это единственный способ сделать это?


person alfalfasprout    schedule 04.01.2017    source источник


Ответы (1)


Дьявол здесь: //updates highest_x, highest_y, highest_z atomically. Как вы гарантируете, что они действительно атомарны? Поскольку 3 двойных значения не помещаются в 16B (самая большая атомарная операция, которую я знаю на платформе X86_64), единственный способ гарантировать это - использовать mutex.

Твоя проблема не в заборе. Выпуская инструкцию по ограждению, вы гарантируете, что все предыдущие обновления будут видны. Однако вы не можете гарантировать, что они не будут видны до этого. В результате вы сможете прочитать самое последнее значение одной из векторных переменных.

Чтобы решить вашу проблему, вы должны либо пойти с mutex - они довольно эффективны, когда без ограничений, - либо, если у вас аллергия на мьютексы, решить проблему подкачки указателя, которую вы описали сами.

person SergeyA    schedule 04.01.2017
comment
Ага. Да, я предполагал, что только собственные атомарные операции могут действительно гарантировать атомарное поведение. Похоже, что замена указателя - это то, что нужно. Я не могу использовать здесь мьютекс или спин-блокировку, так как будет очень серьезная конкуренция, и я хотел бы гарантировать прогресс. - person alfalfasprout; 05.01.2017
comment
Я поискал, но не нашел ссылки на инструкцию CMPXCHG32B. Если такая инструкция действительно существует, в данном случае ее было бы достаточно, так как на сегодняшний день в большинстве систем 3 дубля составляют всего 24 бита. Возможно, вы его перепутали с CMPXCHG16B? В таком случае ваш аргумент имеет гораздо больше смысла. - person Erik Nyström; 12.01.2017
comment
@ ErikNyström, 100% да! Спасибо, что заметили. - person SergeyA; 12.01.2017