Атомарное чтение / запись bool в C#

Является ли доступ к полю bool атомарным в C#? В частности, мне нужно поставить блокировку:

class Foo
{
   private bool _bar;

   //... in some function on any thread (or many threads)
   _bar = true;

   //... same for a read
   if (_bar) { ... }
}

Да, но (возможно) тоже да. Да, доступ / установка поля bool является атомарным, НО операция if - нет (см. Ответ Dror Helper ниже), поэтому вам все равно может понадобиться блокировка.

JPProgrammer 29.09.2015 06:27
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
85
2
57 596
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Да.

Reads and writes of the following data types are atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.

как найдено в Спецификация языка C#.

Обновлено: вероятно, также стоит понять ключевое слово летучий.

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

core 20.09.2008 03:11

Сам указатель, переназначающий его, является атомарным (т.е. Foo foo1 = foo2;

user142350 24.08.2009 19:08

По моему опыту, volatile - это вообще не лучшая идея: если вам нужен volatile, вы обычно заботитесь об «одновременных» действиях - и даже с volatile довольно легко ошибиться. Например, для приведенного выше оператора if потребовалось бы немного дополнительной сантехники, чтобы избежать ввода множества потоков - и если цель состоит только в том, чтобы избежать входа до тех пор, пока какой-либо поток не пройдет _bar = true; хотя бы один раз, отказ от использования volatile в худшем случае означает, что if Оператор не будет выполняться временно, даже если _bar истинен. Я предлагаю просто использовать блокировки, если требуется точность.

Eamon Nerbonne 15.09.2009 17:46

@Eamon: Если дело в том, что if не следует вводить, пока поток не установит _bar = true;, тогда volatile - это именно то, что вам нужно - зачем вам здесь блокировки?

configurator 30.03.2011 17:05

@configurator: Вопрос в том, что именно вам нужно. Легко ошибиться в программах без блокировки; поэтому, если вам это действительно не нужно, лучше использовать более простой фреймворк (например, TPL). Другими словами, «volatile» не является неправильным, а является признаком хитрого (т.е. желательно избегать) кода. OP на самом деле не сказал, что он хочет, я просто волей-неволей сомневаюсь, что рекомендую изменчив.

Eamon Nerbonne 12.04.2011 11:54

Вау. Это опасная формулировка, поскольку атомарность для C++ означает, что любое чтение и запись также окружены соответствующим забором памяти. Что, конечно, не так в C#. Потому что в противном случае производительность была бы ужасной, поскольку она обязательна для всех переменных <long. Атомарно здесь, на языке C#, похоже, означает когда в конечном итоге происходит чтение или запись, они гарантированно никогда не будут в неисправном состоянии. Но он ничего не говорит о том, когда наступит «в конце концов».

v.oddou 25.06.2015 11:35

Если запись в int и long является атомарной, то при использовании Interlocked.Add(ref myInt);, например?

Mike de Klerk 08.12.2015 22:04

@MikedeKlerk Чтение и запись атомарны, но разделены. i++ эквивалентен i=i+1, что означает, что вы выполняете атомарное чтение, затем сложение, а затем атомарную запись. Другой поток может изменить i после чтения, но до записи. Например, два потока, выполняющие i++ одновременно на одном и том же i, могут одновременно читать (и, таким образом, читать одно и то же значение), добавлять к нему одно, а затем оба записывают одно и то же значение, фактически добавляя только один раз. Interlocked.Add предотвращает это. Как правило, тот факт, что тип является атомарным, полезен только в том случае, если есть только один поток записи, но многие потоки читают.

Jannis Froese 21.01.2016 01:59

@Larsenal, ваш ответ неоднозначен, да, это атомарно, и да, мне нужен замок?

johnny 5 24.01.2018 18:32

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

ceztko 15.10.2019 14:05

Доступ к bool действительно атомарен, но это еще не все.

Вам не нужно беспокоиться о чтении значения, которое `` не полностью написано '' - в любом случае не ясно, что это может означать для bool - но вам нужно беспокоиться о кешах процессора, по крайней мере, если детали время является проблемой. Если поток № 1, работающий на ядре A, имеет ваш _bar в кеше, а _bar обновляется потоком № 2, запущенным на другом ядре, поток № 1 не увидит изменения немедленно, если вы не добавите блокировку, не объявите _bar как volatile или явно не вставите вызовы в Thread.MemoryBarrier(), чтобы сделать кешированное значение недействительным.

«в любом случае неясно, что это могло означать для логического типа». Элементы, которые существуют только в одном байте атомарной памяти, потому что весь байт записывается в одно и то же время. По сравнению с такими элементами, как double, которые существуют в нескольких байтах, один байт может быть записан перед другим байтом, и вы можете наблюдать наполовину записанную ячейку памяти.

MindStalker 01.04.2010 23:53

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

TiMoch 04.12.2013 01:05

Барьер памяти полезен, если вы создаете толстый объект и переключаете ссылку на него, которая может быть прочитана из других потоков. Барьер гарантирует, что ссылка не обновляется в основной памяти раньше, чем остальная часть жирного объекта. Гарантируется, что другие потоки никогда не увидят обновление ссылки до того, как толстый объект фактически станет доступным в основной памяти. var fatObject = new FatObject(); Thread.MemoryBarrier(); _sharedRefToFat = fatObject;

TiMoch 04.12.2013 01:06

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

if (b == false)
{
    //do something
}

не атомарная операция, означающая, что значение b может измениться до того, как текущий поток выполнит код после оператора if.

подход, который я использовал и считаю правильным,

volatile bool b = false;

.. rarely signal an update with a large state change...

lock b_lock
{
  b = true;
  //other;
}

... another thread ...

if (b)
{
    lock b_lock
    {
       if (b)
       {
           //other stuff
           b = false;
       }
     }
}

Основная цель заключалась в том, чтобы избежать многократной блокировки объекта на каждой итерации, чтобы проверить, нужно ли блокировать его, чтобы предоставить большой объем информации об изменении состояния, что происходит редко. У меня считать этот подход работает. И если требуется абсолютная согласованность, для b bool будет уместным I считать volatile.

Это действительно правильный подход к блокировке в целом, но если bools атомарны, то проще (и быстрее) опустить блокировку.

dbkk 21.05.2013 13:48

Без блокировки «большое изменение состояния» не будет происходить атомарно. Замок -> установить | Подход check -> lock -> check также гарантирует, что код «// other» выполняется ПЕРЕД кодом «// other stuff». Предполагая, что раздел «другой поток» повторяется много раз (что и происходит в моем случае), большую часть времени нужно только проверять логическое значение, но не получать (возможно, конкурирующую) блокировку, это серьезный выигрыш в производительности.

stux 18.07.2013 09:12

Что ж, если у вас lock(), вам не нужен volatile.

xmedeko 07.02.2018 16:52

Другие вопросы по теме