хитрый InterlockedDecrement против CriticalSection - PullRequest
1 голос
/ 03 августа 2011

Есть глобальный long count счетчик.
Нить А делает

EnterCriticalSection(&crit);
// .... do something
count++;                       // (*1)
// .. do something else
LeaveCriticalSection(&crit);

Тема B делает

InterlockedDecrement(&count); // (*2) not under critical secion.

В (* 1) я нахожусь под критическим разделом. На (* 2) я нет.

Безопасно ли (* 1) без InterlockedIncrement()? (это защищенная критическая секция).
Нужно ли InterlockedIncrement() в (* 1)?
Я чувствую, что могу спорить как за, так и против.

Ответы [ 3 ]

5 голосов
/ 03 августа 2011

Вы должны использовать один или другой, а не смешивать их.

Хотя InterlockedDecrement гарантированно является атомарным, operator++ - нет, хотя в этом случае это, вероятно, будет зависеть от вашей архитектуры. В этом случае вы вообще не защищаете переменную count.

Учитывая, что вы, похоже, хотите выполнять простые операции инкремента / декремента, я бы посоветовал вам просто удалить критический раздел в этом случае и использовать связанные функции Interlocked*.

4 голосов
/ 03 августа 2011

Оба потока должны использовать либо InterlockedDecrement / InterlockedIncrement, или одного и того же критического раздела. Нет оснований для правильного смешивания и сопоставления.

Рассмотрим следующую последовательность событий:

Thread A: enter the critical section
Thread A: read count into a register
Thread A: increment the value in the register
Thread B: InterlockedDecrement(&count) <<< There's nothing to stop this from happening!
Thread A: write the new count
Thread A: leave the critical section

Чистый результат: вы потеряли декремент!

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

И все, что InterlockedDecrement делает, это обеспечивает атомарность декремента. Это не мешает любому другому потоку выполнять вычисления на устаревшей копии переменной и затем записывать результат обратно.

1 голос
/ 03 августа 2011

Да, вы делаете.

В противном случае это может быть:

  1. Значение читается

  2. Значение увеличивается атомно

  3. Исходное значение увеличивается и записывается, что делает недействительным предыдущее атомарное обновление

Кроме того, вам необходим один и тот же критический раздел 1020 * для обоих, поскольку он не помогает блокировать отдельные вещи. :)

...