У меня есть две темы, Продюсер и Потребитель. Обмен данными осуществляется с помощью двух указателей внутри STD :: Атомикс:Как заставить std :: atomic читать после того, как был записан другой std :: atomic?
std::atomic<TNode*> next = nullptr;
std::atomic<TNode*> waiting = nullptr;
Производитель Thread publishs подготовленные данные и после проверяет значение ожидания:
TNode* newNext = new TNode();
// ... fill *newNext ...
next.store(newNext, std::memory_order_release);
TNode* oldWaiting = waiting.load(std::memory_order_seq_cst);
if(oldWaiting == nullptr)
{
/* wake up Consumer */
}
Это crucical, что нагрузка на waiting
приходит после магазина на next
, но std::memory_order_seq_cst
имеет значительно более надежные гарантии, чем мне действительно нужно, так как мне действительно нужен только порядок этих двух доступов. Возможно ли получить заказ на память, который мне нужен, без необходимости в memory_order_seq_cst
?
Вот остальная часть картины:
Thread Потребительские проверки next
. Если он находит его пустым, он устанавливает waiting
, чтобы сигнализировать производителю перед его блокировкой.
TNode* newCurrent = next.load(std::memory_order_consume);
if(newCurrent == nullptr)
{
waiting.store(current, std::memory_order_relaxed);
/* wait, blocking, for next != nullptr */
}
current = newCurrent;
Все это очередь производителей-потребителей, которая сохраняет потребность в блокировке с низким уровнем, не требуя всех сложных механизмов. next
фактически находится внутри текущего узла односвязного списка. Данные обычно поступают в пакеты, так что в большинстве случаев потребитель находит целую кучу узлов, готовых к потреблению; кроме редких случаев, оба потока и только проходят через блокировку и блокировку/пробуждение один раз между очередями.
Как я понимаю, 'memory_order_acquire' ограничивает другие чтения, которые нужно переупорядочить перед загрузкой, но не накладывает никаких ограничений на записи, ни предотвращает их перемещение, читает после загрузки. Или я ошибаюсь? –
Откладывание грузов проще всего понять. Компилятор всегда может задерживать другие нагрузки (при условии, что они не секвенированы сами). Эта задержка может дать только другой результат, если другой поток записывает новые значения в переменную, чья нагрузка задерживается, что означает, что у вас есть условие гонки в любом случае. Заказ памяти не имеет смысла при наличии такого Неопределенного Поведения. Я не думаю, что случайная запись повредит, _unless_, что запись подхватывается другим потоком, используемым в вычислении, а затем используется в вычислении, которое влияет на загрузку с помощью семантики получения. И тогда у вас есть зависимость. – MSalters