Блокировка и неустойчивость

У меня есть переменная, которую я использую для представления состояния. Его можно читать и записывать из нескольких потоков.

Я использую Interlocked.Exchange и Interlocked.CompareExchange, чтобы изменить его. Однако я читаю его из нескольких потоков.

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

Однако, если я устанавливаю переменную в volatile, тогда она генерирует предупреждение об использовании volatile и передаче с использованием ref в Interlocked.

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

Существует Interlocked.Read, но он предназначен для 64-битных типов и недоступен в компактной структуре. В документации для него говорится, что он не нужен для 32-битных типов, поскольку они уже выполняются за одну операцию.

В Интернете есть заявления, которые вам не нужны волатильность, если вы используете методы блокировки для всего вашего доступа. Однако вы не можете прочитать 32-битную переменную, используя методы Interlocked, поэтому вы не можете использовать Interlocked методы для всего вашего доступа.

Есть ли способ выполнить безопасное чтение и запись моей переменной без использования блокировки?

Ответ 1

Вы можете смело игнорировать это предупреждение, когда используете функции Interlocked.Xxx (см. этот вопрос), потому что они всегда выполняют изменчивые операции. Таким образом, переменная volatile отлично подходит для общего состояния. Если вы хотите избавиться от предупреждения любой ценой, вы действительно можете сделать блокированное чтение с помощью Interlocked.CompareExchange (ref counter, 0, 0).

Изменить: На самом деле вам нужно volatile для переменной состояния, только если вы собираетесь писать на нее напрямую (т.е. не используя Interlocked.Xxx). Как упоминалось в jerryjvl, чтение переменной, обновленной с помощью блокированной (или изменчивой) операции, будет использовать самое последнее значение.

Ответ 2

Блокированные операции и изменчивость не должны использоваться одновременно. Причина, по которой вы получаете предупреждение, состоит в том, что она (почти?) Всегда указывает, что вы неправильно поняли, что вы делаете.

Сверх-упрощение и перефразирование:
volatile указывает, что каждая операция чтения должна перечитываться из памяти, потому что могут быть другие потоки, обновляющие переменную. При применении к полю, которое может быть прочитано/записано атомарно по архитектуре, на которой вы работаете, это должно быть все, что вам нужно сделать, если вы не используете long/ulong, большинство других типов могут быть прочитаны/записаны атомарно.

Если поле не отмечено изменчивым, вы можете использовать операции Interlocked, чтобы сделать аналогичную гарантию, поскольку это приводит к покраснению кеша, чтобы обновление было видимым для всех других процессоров... это имеет преимущество что вы накладываете накладные расходы на обновление, а не на чтение.

Какой из этих двух подходов работает лучше всего, зависит от того, что именно вы делаете. И это объяснение - грубое чрезмерное упрощение. Но из этого должно быть ясно, что делать оба одновременно бессмысленно.