Итак, требования:
- несколько потоков читают одну
bool
переменную
- если поток видит значение
false
, он должен изменить его на true
и выполнить дополнительную работу
- если поток видит значение
true
, больше ничего не должно происходить
- только один поток должен иметь возможность изменить значение и выполнить дополнительную работу
Таким образом, код будет выглядеть так:
public class Example
{
private bool b = false;
public void DoSomething()
{
if (!b)
{
b = true;
DoExtraStuff();
}
}
}
Это, конечно, небезопасно, если есть несколько потоков, выполняющих DoSomething
, и предполагается, что DoExtraStuff
должен выполняться только один раз. Проблема в том, что потоки будут состязаться при чтении и записи b
.
Вам действительно нужно сделать чтение и запись в b
атомарными. Это можно сделать с помощью ключевого слова lock
.
public class Example
{
private bool b = false;
private object locker = new object();
public void DoSomething()
{
bool trigger = false;
lock (locker)
{
if (!b)
{
b = true;
trigger = true;
}
}
if (trigger)
{
DoExtraStuff();
}
}
}
Существует альтернативный шаблон с использованием операции CAS через Interlocked.CompareExhange
. К сожалению, нет перегрузки, которая принимает bool
, но если вы хотите изменить это bool
на int
, то также сработает следующее.
public class Example
{
private int b = 0;
public void DoSomething()
{
if (Interlocked.CompareExchange(ref b, 0, 1) == 0)
{
DoExtraStuff();
}
}
}