Блокируемый и изменчивый


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

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

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

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

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

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

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

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

2 61

2 ответа:

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

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

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

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

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

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