Как гарантировать, что загрузка завершится до того, как произойдет сохранение? - PullRequest
2 голосов
/ 26 февраля 2020

В следующем коде, как можно гарантировать, что ptr не увеличивается до тех пор, пока * ptr не будет загружен / назначен / "извлечен"?

extern int arr[some_constexpr]; // assume pre-populated
extern int* ptr; // assume points to non-atomic arr
int a = *ptr;
// want "memory barrier/fence" here
++ptr;

Будет ли указатель атома c обеспечить правильное упорядочение / sequencing?

#include <atomic>

extern int arr[some_constexpr];
extern std::atomic<int*> ptr;
int a = *(ptr.load());
// implicit "memory barrier" achieved here by use of atomics?
ptr.store(ptr + 1);

Это относится к очереди без блокировки, совместно используемой двумя потоками. Я хочу убедиться, что данные, связанные с указателем, не будут потеряны / повреждены перед обновлением указателя.

Ответы [ 2 ]

1 голос
/ 27 февраля 2020

Если ptr равно std::atomic<int*>, ++ptr или ptr++ или ptr.fetch_add(1, std::memory_order_acq_rel), убедитесь, что ни одна из предшествующих / последующих загрузок / хранилищ не будет переупорядочена после / до этой операции.

++ptr или ptr++ по существу ptr.fetch_add(1, std::memory_order_seq_cst), а std::memory_order_seq_cst почти всегда является избыточным убийством (не может привести пример, где это не так).

Еще более эффективный одноместный считыватель:

int arr[some_constexpr];
std::atomic<int*> ptr;

int* p = ptr.load(std::memory_order_acquire);
int element = *p;
ptr.store(p + 1, memory_order_release);

В основном выше описан способ реализации boost::lockfree::spsc_queue.


В качестве примечания, boost::lockfree::spsc_queue является истинной без ожидания (самая сильная неблокирующая гарантия прогресса) очереди. Операции push / pop - это загрузка 1 relaxed, загрузка 1 acquire и хранение 1 release, и принципиально невозможно реализовать очередь одного производителя-одного потребителя быстрее, чем эта (без дефекты реализации) с гарантией заказа FIFO. Он часто используется в качестве теста для всех остальных очередей . Вы можете посмотреть на это.

0 голосов
/ 27 февраля 2020

Согласно разговору Херба Саттера (который я настоятельно рекомендую, чтобы попытаться понять этот материал), например, посмотрите в момент времени 36: 25-45: 25, приведенный в ОП пример атома c Достаточно убедиться, что a назначено до увеличения ptr.

Это потому, что увеличение ptr является хранилищем (так как оно записывается), и, следовательно, "release", учитывая память по умолчанию порядок std::memory_order_seq_cst (но в этом случае также применяется порядок порядка *_acq_rel или *_release). Это означает, что ничто, происходящее до приращения / сохранения / выпуска ptr, не может быть переупорядочено, чтобы происходить после приращения / хранения / выпуска ptr, явно или неявно компилятором, процессором и / или кешем.

Никаких явных ограничений памяти не требуется, кроме тех, которые подразумеваются при загрузке / хранении атома c, и они фактически не приветствуются в разговоре.

...