Чи є bool читанням / записом атомарним на C #


84

Чи є доступ до поля bool атомним у C #? Зокрема, чи потрібно мені ставити замок навколо:

class Foo
{
   private bool _bar;

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

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


1
Так, але (можливо) також так. Так, доступ / налаштування поля bool є атомарним, АЛЕ якщо операція не виконується (див. Відповідь Dror Helper нижче), тож вам все одно може знадобитися блокування.
JPProgrammer

Відповіді:


119

Так.

Читає та записує такі типи даних атомарними: bool, char, byte, sbyte, short, ushort, uint, int, float та reference reference.

як знайдено в мові C # Spec .

Редагувати: Напевно, також варто розуміти нестабільне ключове слово.


10
Сам покажчик, перепризначаючи його, є атомним (тобто Foo foo1 = foo2;
user142350

4
@configurator: Питання в тому, що саме ти хочеш. Легко помилитися з програмами без блокування; тому, якщо вам це дійсно не потрібно, краще використовувати більш простий фреймворк (наприклад, TPL). Іншими словами, "нестабільний" - це не неправильно, а ознака хитрого (тобто бажано уникати) коду. ОП насправді не сказав, що він хоче, я просто вагаюся рекомендувати мінливих волею-неволею.
Eamon Nerbonne

4
Вау. Це небезпечне формулювання, оскільки для людей, що працюють на C ++, атомні засоби означає, що будь-який запис для читання також оточений відповідним парканом пам'яті. Що, звичайно, не так у C #. Тому що в іншому випадку продуктивність буде жахливою, оскільки є обов’язковою для всіх змінних <long. Здається, атомність тут, у мові C #, означає, що, коли зрештою відбувається читання або запис, вони гарантовано ніколи не будуть у порушеному стані . Але це нічого не говорить про те, коли "врешті-решт".
v.oddou

4
Якщо запис у int та long є атомним, то коли використовувати Interlocked.Add(ref myInt);eg?
Mike de Klerk

6
@MikedeKlerk Читання та запис є атомними, але окремими. i++дорівнює i=i+1, тобто ви робите атомне читання, потім додавання, а потім атомне записування. Інший потік може бути змінений iпісля прочитання, але перед записом. Наприклад, два потоки, які i++одночасно виконують одне і те ж i, можуть прочитати одночасно (і, отже, прочитати одне і те ж значення), додати до нього одне, а потім обидва записати одне і те ж значення, фактично додавши лише один раз. Блокується. Додати запобігає цьому. Як загальне правило, той факт, що тип є атомним, корисний лише в тому випадку, якщо існує лише одне написання потоку, але багато потоків читає.
Jannis Froese

49

Як було зазначено вище, бул є атомним, але все одно потрібно пам'ятати, що це також залежить від того, що ви хочете з ним робити.

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

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


29

bool-доступ справді є атомним, але це ще не вся історія.

Вам не потрібно турбуватися про читання значення, яке написано "не повністю" - незрозуміло, що це може означати для bool у будь-якому випадку, - але вам доведеться турбуватися про кеш процесора, принаймні, якщо питання часу. Якщо нитка # 1 працює на ядрі A має ваш _barв кеші, і _barоновлюється по темі # 2 працює на іншому ядрі, нитка # 1 не бачитиме зміни негайно , якщо не додати блокування, оголосіть в _barякості volatile, або в явному вигляді вставки викликів Thread.MemoryBarrier()в знецінюють кешоване значення.


1
"незрозуміло, що це може означати для bool у будь-якому випадку" Елементи, які існують лише в одному байті пам'яті в атомному, оскільки весь байт записаний одночасно. У порівнянні з такими елементами, як double, які існують у декількох байтах, один байт може бути записаний перед другим байтом, і ви можете спостерігати наполовину записане розташування пам'яті.
MindStalker

3
MemoryBarrier () не робить кеш кеш-пам’яті процесора. У деяких архітектурах процесору дозволяється змінювати порядок читання та запису в основну пам’ять для підвищення продуктивності. Переупорядкування може відбуватися до тих пір, поки з точки зору одного потоку семантика залишається незмінною. MemoryBarrier () просить процесор обмежити переупорядкування, щоб операції з пам'яттю, випущені перед бар'єром, не були впорядковані таким чином, що вони закінчуються після бар'єру.
TiMoch

1
Бар'єр пам'яті корисний, якщо ви створюєте жирний об'єкт і перемикаєте посилання на нього, яке можна прочитати з інших потоків. Бар'єр гарантує, що посилання не оновлюється в основній пам'яті до решти об'єкту жиру. Інші потоки гарантовано ніколи не побачать оновлення посилань до того, як об’єкт жиру стане фактично доступним в основній пам’яті. var fatObject = new FatObject(); Thread.MemoryBarrier(); _sharedRefToFat = fatObject;
TiMoch

1

підхід, який я використав, і я вважаю правильним, є

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.


4
Це справді правильний підхід до блокування загалом, але якщо були атомні, то простіше (і швидше) опустити блокування.
dbkk

2
Без блокування "велика зміна стану" не буде здійснена атомно. Замок -> встановити | перевірка -> блокування -> перевірка також забезпечить виконання коду "// інший" ДО коду "// інші речі". Якщо припустити, що розділ "інший потік" повторюється багато разів (що і є в моєму випадку), більшість випадків потрібно лише перевіряти bool, але насправді не отримувати (можливо, передбачуваний) замок, - це основна перемога в продуктивності
stux

1
Ну, а якщо є lock(), то не потрібно volatile.
xmedeko
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.