Изменить в связи с поспешным ответом в обеденное время ..
Оператор блокировки, использованный в предыдущем коде, блокирует экземпляр объекта, созданный в области действия метода, поэтому он не будет влиять на другой поток, вызывающий тот же метод. Каждый поток должен иметь возможность блокировать один и тот же экземпляр объекта, чтобы синхронизировать доступ к данному блоку кода. Один из способов сделать это (в зависимости от требуемой семантики) - сделать объект блокировки частной статической переменной класса, в котором он используется. Это позволит нескольким экземплярам данного объекта синхронизировать доступ к блоку кода или один общий ресурс. Если синхронизация требуется для отдельных экземпляров объекта или ресурса, который специфичен для конкретного экземпляра, то должна генерироваться статическая.
Volatile не гарантирует, что чтение или запись в данную переменную будут атомарными среди разных потоков. Это подсказка компилятора для сохранения порядка команд и предотвращения кэширования переменной внутри регистра. В общем, если вы не работаете над чем-то чрезвычайно чувствительным к производительности (алгоритмы с низкой блокировкой / без блокировок, структуры данных и т. Д.) Или действительно знаете, что делаете, я бы выбрал Interlocked. Разница в производительности между использованием volatile / interlocked / lock в большинстве приложений будет незначительной, поэтому, если вы не уверены, что лучше всего использовать то, что дает вам самую безопасную гарантию (читайте блог Джо Даффи и book ).
Например, использование volatile в приведенном ниже примере не является потокобезопасным, и приращенный счетчик не достигает 10 000 000 (когда я запустил тест, он достиг 8848450). Это потому, что volatile гарантирует только чтение последнего значения (например, не кешируется из регистра, например). При использовании блокировок операция является поточно-ориентированной, и счетчик достигает 10 000 000.
public class Incrementor
{
private volatile int count;
public int Count
{
get { return count; }
}
public void UnsafeIncrement()
{
count++;
}
public void SafeIncrement()
{
Interlocked.Increment(ref count);
}
}
[TestFixture]
public class ThreadingTest
{
private const int fiveMillion = 5000000;
private const int tenMillion = 10000000;
[Test]
public void UnsafeCountShouldNotCountToTenMillion()
{
const int iterations = fiveMillion;
Incrementor incrementor = new Incrementor();
Thread thread1 = new Thread(() => UnsafeIncrement(incrementor, iterations));
Thread thread2 = new Thread(() => UnsafeIncrement(incrementor, iterations));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Assert.AreEqual(tenMillion, incrementor.Count);
}
[Test]
public void SafeIncrementShouldCountToTenMillion()
{
const int iterations = fiveMillion;
Incrementor incrementor = new Incrementor();
Thread thread1 = new Thread(() => SafeIncrement(incrementor, iterations));
Thread thread2 = new Thread(() => SafeIncrement(incrementor, iterations));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Assert.AreEqual(tenMillion, incrementor.Count);
}
private void UnsafeIncrement(Incrementor incrementor, int times)
{
for (int i =0; i < times; ++i)
incrementor.UnsafeIncrement();
}
private void SafeIncrement(Incrementor incrementor, int times)
{
for (int i = 0; i < times; ++i)
incrementor.SafeIncrement();
}
}
Если вы ищете «изменчивая блокировка», вы найдете несколько ответов на свой вопрос. Например, приведенный ниже ответ на ваш вопрос:
Простой пример ниже показывает
Летучий против блокировки и против блокировки