2016-07-05 3 views
0

Я играю с атомными чтениями и пишет в настоящее время и ударил стену в своем понимании. Я понимаю, что запись в переменную (например, через приращение) должна быть атомарной, но я не уверен в чтении переменной.Функции для атомарного считывания значения?

Рассмотрите _InterlockedExchangeAdd на Windows, или __sync_add_and_fetch на Linux. Я не могу найти функцию, которая атомарно извлекает значение, которое обновляется. Теперь я провел свое исследование перед публикацией здесь, и Are C++ Reads and Writes of an int Atomic? говорит мне, что чтение не является атомарным.

1) Если я использую перечисленные выше функции, как я атомизирую , прочитайте значение, скажем, если возвращаете его из функции?

2) Если бы я не хотел использовать эти функции и просто хотел заблокировать мьютекс перед каждой записью моей «атомной» переменной, то в функции, которая извлекает текущее значение переменной, мне нужно сначала заблокировать мьютексы, скопировать текущее значение, разблокировать мьютексы и вернуть копию?

EDIT

Я использую компилятор, который не имеет доступ к атомным заголовкам, следовательно, должен использовать этот API.

+1

Вы изучали использование [''] (http://en.cppreference.com/w/cpp/atomic/atomic)? – CoryKramer

+0

Извините, я обновил Q, чтобы показать, что я не могу использовать атомный заголовок. – Wad

+0

Если вы выполняете чтение-изменение-запись, вся операция должна находиться внутри мьютекса. Одной из худших ошибок, которые я должен был отследить, был тот, кто думал, что до тех пор, пока чтение будет в мьютексе, и так было написано, что он мог выпустить его между ними. – stark

ответ

1

Вы не можете найти ответ, потому что не было ни одного способа сделать это, чтобы он был (а) быстрым и (б) портативным. Это зависит от: C++ или C, компилятора, версии компилятора, настроек компилятора, библиотеки, архитектуры ... список можно продолжать и продолжать.

Вот отправная точка:

  • Если у вас есть доступ к стандартному Атомиксу или используйте atomic_load() - это портативная и быстро.
  • Если у вас нет стандартных атомов, используйте CAS. Например, в Windows это будет _InterlockedCompareExchange(,0,0). См. https://msdn.microsoft.com/en-us/library/ms686355(VS.85).aspx. В Linux это будет __sync_val_compare_and_swap(, 0, 0). См. https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html.
  • Если вы хотите использовать прямое назначение - тогда вы должны проверить у эксперта, который знает, где будет работать ваш код.

У меня есть фрагмент кода ассемблера, который может объяснить, почему CAS является разумной альтернативой. Это C, i86, Microsoft компилятор VS2015, Win64 цель:

volatile long debug_x64_i = std::atomic_load((const std::_Atomic_long *)&my_uint32_t_var); 
00000001401A6955 mov   eax,dword ptr [rbp+30h] 
00000001401A6958 xor   edi,edi 
00000001401A695A mov   dword ptr [rbp-0Ch],eax 
    debug_x64_i = _InterlockedCompareExchange((long*)&my_uint32_t_var, 0, 0); 
00000001401A695D xor   eax,eax 
00000001401A695F lock cmpxchg dword ptr [rbp+30h],edi 
00000001401A6964 mov   dword ptr [rbp-0Ch],eax 
    debug_x64_i = _InterlockedOr((long*)&my_uint32_t_var, 0); 
00000001401A6967 prefetchw [rbp+30h] 
00000001401A696B mov   eax,dword ptr [rbp+30h] 
00000001401A696E xchg  ax,ax 
00000001401A6970 mov   ecx,eax 
00000001401A6972 lock cmpxchg dword ptr [rbp+30h],ecx 
00000001401A6977 jne   foo+30h (01401A6970h) 
00000001401A6979 mov   dword ptr [rbp-0Ch],eax 

    volatile long release_x64_i = std::atomic_load((const std::_Atomic_long *)&my_uint32_t_var); 
00000001401A6955 mov   eax,dword ptr [rbp+30h] 
    release_x64_i = _InterlockedCompareExchange((long*)&my_uint32_t_var, 0, 0); 
00000001401A6958 mov   dword ptr [rbp-0Ch],eax 
00000001401A695B xor   edi,edi 
00000001401A695D mov   eax,dword ptr [rbp-0Ch] 
00000001401A6960 xor   eax,eax 
00000001401A6962 lock cmpxchg dword ptr [rbp+30h],edi 
00000001401A6967 mov   dword ptr [rbp-0Ch],eax 
    release_x64_i = _InterlockedOr((long*)&my_uint32_t_var, 0); 
00000001401A696A prefetchw [rbp+30h] 
00000001401A696E mov   eax,dword ptr [rbp+30h] 
00000001401A6971 mov   ecx,eax 
00000001401A6973 lock cmpxchg dword ptr [rbp+30h],ecx 
00000001401A6978 jne   foo+31h (01401A6971h) 
00000001401A697A mov   dword ptr [rbp-0Ch],eax 

Ваш план (2) для использования мьютекса является правильным.

Удачи.

Смежные вопросы