При использовании ключевого слова lock, кажется, требуется меньший ввод программного кода, чем при явном использований типа System.Threading.Monitor, поэтому вы можете задать вопрос о преимуществах непосредственного использования типа Monitor. Краткий ответ: контроль. При использовании типа Monitor вы можете дать указание активному потоку подождать (с помощью метода Wait), информировать ожидающие потоки о завершении текущего потока (с помощью методов Pulse и PulseAll) и т.д.

В большинстве случаев вам будет вполне достаточно возможностей, обеспечиваемых ключевым словам C# lock. Но если вы захотите рассмотреть другие члены класса Monitor, обратитесь к документации .NET Framework 2.0 SDK.

<p>Синхронизация с помощью типа System.Threading.Interlocked</p>

В это всегда верится с трудом, пока вы не проверите соответствующий программный код CIL, но и операции присваивания, и базовые арифметические операции не являются атомарными. Поэтому в пространстве имен System.Threading предлагается тип, позволяющий воздействовать на отдельный элемент данных атомарно с меньшей нагрузкой, чем это делает тип Monitor. Тип класса Interlocked определяет статические члены, описания которых приведены в табл. 14.4.

Таблица 14.4. Члены типа System.Threading.Interlocked

Член Описание
CompareExchange Безопасно проверяет два значения на равенство, и если они равны, заменяет одно из значений третьим
Decrement Безопасно уменьшает значение на 1
Exchange Безопасно меняет два значения местами
Increment Безопасно выполняет приращение значения на 1

Хотя это может и не казаться очевидным на первый взгляд, процесс атомарного изменения одного значения является вполне типичным в многопоточном окружении. Предположим, что у нас есть метод AddOne, который увеличивает целочисленную переменную intVal на единицу. Вместо программного кода синхронизации, подобного следующему;

public void AddOne {

 lock(this) {

  intVal++;

 }

}

можно предложить более простой программный код, в котором используется статический метод Interlocked.Increment. Просто передайте переменную для приращения по ссылке. Обратите внимание на то, что метод Increment не только изменяет значение поступающего параметра, но и возвращает новое значение.

public void AddOne {

 int newVal = Interlocked.Increment(ref intVal);

}

В дополнение к Increment и Decrement тип Interlocked позволяет атомарно присваивать числовые и объектные данные. Например, если вы хотите присвоить члену-переменной значение 83, вы можете избежать необходимости явного использования оператора lock (или явного применения логики Monitor), если используете метод Interlocked.Exchange.

public void SafeAssignment {

 Interlocked.Exchange(ref myInt, 83);

}

Наконец, при проверке двух значений на равенство, чтобы обеспечить потоковую безопасность элементу сравнения, можете использовать метод Interlocked.CompareExchange, как показано ниже.

public void CompareAndExchange {

 // Если значением i является 83, изменить его на 99.

 Interlocked.CompareExchange(ref i, 99, 83);

}

<p>Синхронизация с помощью атрибута [Synchronization]</p>

Последним из рассмотренных здесь примитивов синхронизации будет атрибут [Synchronization], который определяется в пространстве имен System.Runtime.Remoting.Contexts. Этот атрибут уровня класса для безопасности потока эффективно блокирует весь программный код членов экземпляра. Когда среда CLR размещает объект, имеющий атрибут [Synchronization], она помещает этот объект в рамки синхронизированного контекста. Вы должны помнить из главы 13, что объекты, которые не должны покидать контекстные границы, являются производными от ContextBoundObject. Поэтому, чтобы сделать тип класса Printer устойчивым в отношении потоков (без добавления программного кода защиты потоков вручную), следует изменить соответствующее определение так.

using System.Runtime.Remoting.Contexts;

...

// Все методы Printer теперь потокоустойчивы!

[Synchronization]

public class Printer: СontextBoundObject {

 public void PrintNumbers {

 …

 }

}

Перейти на страницу:

Похожие книги