Ниже подведены краткие итоги использования блокировки.

• Если блокировка любого заданного объекта получена в одном потоке, то после блокировки объекта она не может быть получена в другом потоке.

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

• Когда поток выходит из заблокированного фрагмента кода, соответствующий объект разблокируется.

Другой подход к синхронизации потоков

Несмотря на всю простоту и эффективность блокировки кода метода, как показано в приведенном выше примере, такое средство синхронизации оказывается пригодным далеко не всегда. Допустим, что требуется синхронизировать доступ к методу класса, который был создан кем-то другим и сам не синхронизирован. Подобная ситуация вполне возможна при использовании чужого класса, исходный код которого недоступен. В этом случае оператор lock нельзя ввести в соответствующий метод чужого класса. Как же тогда синхронизировать объект такого класса? К счастью, этот вопрос разрешается довольно просто: доступ к объекту может быть заблокирован из внешнего кода по отношению к данному объекту, для чего достаточно указать этот объект в операторе lock. В качестве примера ниже приведен другой вариант реализации предыдущей программы. Обратите внимание на то, что код в методе Sumlt () уже не является заблокированным, а объект lockOn больше не объявляется. Вместо этого вызовы метода Sumlt () блокируются в классе MyThread.

// Другой способ блокировки для синхронизации доступа к объекту, using System;

Using System.Threading;

class SumArray { int sum;

public int Sumlt(int[] nums) {

sum =0; // установить исходное значение суммы

for(int i=0; i < nums.Length; i++) { sum += nums[i];

Console.WriteLine("Текущая сумма для потока " +

Thread.CurrentThread.Name + " равна " + sum); Thread.Sleep(10); // разрешить переключение задач

}

Return sum;

}

}

class MyThread {

public Thread Thrd; int[] a; int answer;

/* Создать один объект типа SumArray для всех экземпляров класса MyThread. */ static SumArray sa = new SumArray();

// Сконструировать новый поток, public MyThread(string name, int[] nums) { a = nums;

Thrd = new Thread(this.Run);

Thrd.Name = name;

Thrd.Start(); // начать поток

}

// Начать выполнение нового потока, void Run() {

Console.WriteLine(Thrd.Name + " начат.");

// Заблокировать вызовы метода Sumlt(). lock(sa) answer = sa.Sumlt(a);

Console.WriteLine("Сумма для потока " + Thrd.Name +

" равна " + answer);

Console.WriteLine(Thrd.Name + " завершен.");

}

}

class Sync {

static void Main() {

int[] a = {1, 2, 3, 4, 5};

MyThread mtl = new MyThread("Потомок #1", a);

MyThread mt2 = new MyThread("Потомок #2", a);

Mtl.Thrd.Join(); mt2.Thrd.Join();

В данной программе блокируется вызов метода sa . Sum It () , а не сам метод Sum It () . Ниже приведена соответствующая строка кода, в которой осуществляется подобная блокировка.

// Заблокировать вызовы метода Sumlt() . lock(sa) answer = sa.Sumlt(a);

Объект sa является закрытым, и поэтому он может быть благополучно заблокирован. При таком подходе к синхронизации потоков данная программа дает такой же правильный результат, как и при первоначальном подходе.

Класс Monitor и блокировка

Ключевое слово lock на самом деле служит в C# быстрым способом доступа к средствам синхронизации, определенным в классе Monitor, который находится в пространстве имен System. Threading. В этом классе определен, в частности, ряд методов для управления синхронизацией. Например, для получения блокировки объекта вызывается метод Enter () , а для снятия блокировки — метод Exit ( ). Ниже приведены общие формы этих методов:

public static void Enter(object obj) public static void Exit (object obj)

где obj обозначает синхронизируемый объект. Если же объект недоступен, то после вызова метода Enter () вызывающий поток ожидает до тех пор, пока объект не станет доступным. Тем не менее методы Enter () и Exit () применяются редко, поскольку оператор lock автоматически предоставляет эквивалентные средства синхронизации потоков. Именно поэтому оператор lock оказывается "более предпочтительным" для получения блокировки объекта при программировании на С#.