Ключевое слово lock
не блокирует переменные. Он определяет часть кода, которая может быть выполнена только в одном потоке за раз. Например, из этих двух кодовых блоков только один может выполняться в любой момент и только в одном потоке:
lock (lockObject)
{
this.A = random.Next();
this.B = random.Next();
this.C = this.A + this.B;
}
lock (lockObject)
{
Console.WriteLine("{0}+{1}={2}", this.A, this.B, this.C);
}
Таким образом, в любой данный момент только один поток читает или записывает переменные-члены A, B и C.
Что бы произошло, если бы мы сняли замки? Операционная система использует упреждающую многопоточность, поэтому вы не можете гарантировать, что ваш код не будет прерван в любой момент, между любыми двумя строками кода (а иногда даже во время одной). Так, например, второй кодовый блок может находиться между любыми двумя строками в первом кодовом блоке. Посмотрим ...
this.A = random.Next();
this.B = random.Next();
Console.WriteLine("{0}+{1}={2}", this.A, this.B, this.C);
this.C = this.A + this.B;
Как вы можете ясно видеть, эта программа плохо работает по математике.
При этом, мы можем смягчить проблему без каких-либо блокировок, будучи умными в том, как мы назначаем вещи.
var results = new SomeClass
{
A = random.Next(),
B = random.Next()
};
results.C = results.A + results.B;
this.LatestResults = results;
Теперь единственная общая переменная (поскольку каждый поток получает свои локальные переменные) - LatestResults
. Который может быть обновлен в одной атомарной операции. В этом случае блокировка не требуется.