Чи є доступ до поля 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 атомним у C #? Зокрема, чи потрібно мені ставити замок навколо:
class Foo
{
private bool _bar;
//... in some function on any thread (or many threads)
_bar = true;
//... same for a read
if (_bar) { ... }
}
Відповіді:
Так.
Читає та записує такі типи даних атомарними: bool, char, byte, sbyte, short, ushort, uint, int, float та reference reference.
як знайдено в мові C # Spec .
Редагувати: Напевно, також варто розуміти нестабільне ключове слово.
Interlocked.Add(ref myInt);
eg?
i++
дорівнює i=i+1
, тобто ви робите атомне читання, потім додавання, а потім атомне записування. Інший потік може бути змінений i
після прочитання, але перед записом. Наприклад, два потоки, які i++
одночасно виконують одне і те ж i, можуть прочитати одночасно (і, отже, прочитати одне і те ж значення), додати до нього одне, а потім обидва записати одне і те ж значення, фактично додавши лише один раз. Блокується. Додати запобігає цьому. Як загальне правило, той факт, що тип є атомним, корисний лише в тому випадку, якщо існує лише одне написання потоку, але багато потоків читає.
Як було зазначено вище, бул є атомним, але все одно потрібно пам'ятати, що це також залежить від того, що ви хочете з ним робити.
if(b == false)
{
//do something
}
не є атомною операцією, що означає, що значення b може змінитися до того, як поточний потік виконає код після оператора if.
bool-доступ справді є атомним, але це ще не вся історія.
Вам не потрібно турбуватися про читання значення, яке написано "не повністю" - незрозуміло, що це може означати для bool у будь-якому випадку, - але вам доведеться турбуватися про кеш процесора, принаймні, якщо питання часу. Якщо нитка # 1 працює на ядрі A має ваш _bar
в кеші, і _bar
оновлюється по темі # 2 працює на іншому ядрі, нитка # 1 не бачитиме зміни негайно , якщо не додати блокування, оголосіть в _bar
якості volatile
, або в явному вигляді вставки викликів Thread.MemoryBarrier()
в знецінюють кешоване значення.
var fatObject = new FatObject(); Thread.MemoryBarrier(); _sharedRefToFat = fatObject;
підхід, який я використав, і я вважаю правильним, є
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.
lock()
, то не потрібно volatile
.