блокированное чтение 64-разрядной переменной

#c #multithreading

#c #многопоточность #c

Вопрос:

У меня есть этот код на c (по сравнению с 2008):

 LONGLONG res = InterlockedIncrement64(amp;m_longlong);
  

пробегая по нему, я хотел бы иметь возможность читать из той же переменной

 LONGLONG res = InterlockedWHAT?64(amp;m_longlong)
  

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

Как я должен прочитать эту переменную?

Комментарии:

1. в 64-битной архитектуре выровненное 64-битное чтение (QWORD) должно быть атомарным, IIRC. Посмотрите на tr1 / boost / c 0x atomic<int64_t> для переносимого решения без стресса

2. спасибо, но я предпочел бы не ссылаться на совершенно новую библиотеку прямо сейчас. ищу решение InterlockedXXX

3. идея комментария в том, что это НЕ ответ 🙂 Я просто излагал свои мысли. Если вы знаете арку, вы могли бы просто убедиться в правильности выравнивания и все готово. Вопрос недостаточно сформулирован, вот почему это не ответ.

4. @sehe: Вы не можете просто прочитать выровненную QWORD . Для обеспечения безопасности параллелизма вам нужны две вещи: атомарность и порядок записи / чтения. Даже если 64-разрядный процессор будет считывать значение атомарно, оптимизатор или центральный процессор могут неожиданным образом повлиять на чтение.

Ответ №1:

 LONGLONG res = InterlockedCompareExchange64(amp;m_longlong, 0, 0);
  

Комментарии:

1. @user991339 — другой поток может изменять значение во время его чтения для ввода в стек в качестве второго параметра. Возвращаемое значение будет в порядке, но общая переменная тогда будет содержать мусор.

2. InterlockedExchange64 не годится. Вам пришлось бы назвать это как InterlockedExchange64(amp;val, val) , и теперь вы должны увидеть проблему. Как вы считываете значение val для передачи в качестве второго параметра? Это та же проблема, с которой вы начали.

3. @Henrik — это может произойти и с InterlockedCompareExchange, когда m_longlong == 0 и 0 помещаются в стек, в то время как другой поток записывает какое-то другое значение, не так ли?

4. Нет, это невозможно. Если m_longlong == 0 тогда m_longlong будет установлено значение 0 (т. Е. без изменений), или иным образом m_longlong не будет изменено. Другими словами, код в этом ответе не может быть изменен m_longlong .

5. Вы должны выбрать значение, которое m_longlong вряд ли будет иметь, чтобы избежать загрязнения строки кэша.

Ответ №2:

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

Комментарии:

1. Обратите внимание, что это изменяет значение (записывает результат обратно, даже если он численно идентичен), что может вызвать проблемы с производительностью, поскольку загрязняет строку кэша.

2. @Raymond Спасибо, что добавили это. Каким было бы ваше предпочтительное решение вопроса?

3. Я бы использовал InterlockedCompareExchange с маловероятным значением. blogs.msdn.com/10152296.aspx

Ответ №3:

 LONGLONG res = InterlockedOr64(amp;m_longlong, 0);
  

Если ваша программа работает только на 64-разрядной версии, вы можете просто прочитать значение. MSDN утверждает, что

Простые операции чтения и записи в правильно выровненные 64-разрядные переменные являются атомарными в 64-разрядной Windows.

Ответ №4:

С VS 2019 вы должны использовать __iso_volatile_load64 , который является простой загрузкой, поэтому он более эффективен, чем InterlockedCompareExchange64