Using System.Runtime.CompilerServices;

// Вариант класса TickTock, переделанный с целью // использовать атрибут MethodlmplOptions.Synchronized, class TickTock {

/* Следующий атрибут полностью синхронизирует метод Tick(). */ [MethodlmplAttribute(MethodlmplOptions.Synchronized)] public void Tick (bool running) { if(!running) { // остановить часы

Monitor.Pulse(this); // уведомить любые ожидающие потоки return;

}

Console.Write("тик ");

Monitor.Pulse(this); // разрешить выполнение метода Tock()

Monitor.Wait(this); // ожидать завершения метода Tock()

}

/* Следующий атрибут полностью синхронизирует метод Тоск(). */ [MethodlmplAttribute(MethodlmplOptions.Synchronized)] public void Tock(bool running) { if (!running) { // остановить часы

Monitor.Pulse(this); // уведомить любые ожидающие потоки return;

}'

Console.WriteLine("так");

Monitor.Pulse(this); // разрешить выполнение метода Tick()

Monitor.Wait(this); // ожидать завершения метода Tick()

}

}

class MyThread {

Public Thread Thrd;

TickTock ttOb;

// Сконструировать новый поток.

public MyThread(string name, TickTock tt) {

Thrd = new Thread(this.Run); ttOb = tt;

Thrd.Name = name;

Thrd.Start();

}

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

if(Thrd.Name == "Tick") {

for(int i=0; i<5; i++) ttOb.Tick(true); ttOb.Tick(false);

}

else {

for(int i=0; i<5; i++) ttOb.Tock(true); ttOb.Tock(false);

}

}

}

class TickingClock { static void Main() {

TickTock tt = new TickTock();

MyThread mtl = new MyThread("Tick", tt) ;

MyThread mt2 = new MyThread("Tock", tt) ;

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

Console.WriteLine("Часы остановлены");

}

}

Эта версия программы дает такой же результат, как и предыдущая. Синхронизируемый метод не определен в открытом классе и не вызывается для открытого объекта, поэтому применение оператора lock или атрибута MethodlmplAttribute зависит от личных предпочтений. Ведь и тот и другой дает один и тот же результат. Но поскольку ключевое слово lock относится непосредственно к языку С#, то в примерах, приведенных в этой книге, предпочтение отдано именно ему.

ПРИМЕЧАНИЕ

Не применяйте атрибут MethodlmplAttribute в открытых классах или экземплярах открытых объектов. Вместо этого пользуйтесь оператором lock, чтобы заблокировать метод для закрытого объекта, как пояснялось ранее.

Применение мьютекса и семафора

В большинстве случаев, когда требуется синхронизация, оказывается достаточно и оператора lock. Тем не менее в некоторых случаях, как, например, при ограничении доступа к общим ресурсам, более удобными оказываются механизмы синхронизации, встроенные в среду .NET Framework. Ниже рассматриваются по порядку два таких механизма: мьютекс и семафор.

Мьютекс

Мьютекс представляет собой взаимно исключающий синхронизирующий объект. Это означает, что он может быть получен потоком только по очереди. Мьютекс предназначен для тех ситуаций, в которых общий ресурс может быть одновременно использован только в одном потоке. Допустим, что системный журнал совместно используется в нескольких процессах, но только в одном из них данные могут записываться в файл этого журнала в любой момент времени. Для синхронизации процессов в данной ситуации идеально подходит мьютекс.

Мьютекс поддерживается в классе System. Threading.Mutex. У него имеется несколько конструкторов. Ниже приведены два наиболее употребительных конструктора.

Public Mutex()

public Mutex(bool initiallyOwned)

В первой форме конструктора создается мьютекс, которым первоначально никто не владеет. А во второй форме исходным состоянием мьютекса завладевает вызывающий поток, если параметр ini tiallyOwned имеет логическое значение true. В противном случае мьютексом никто не владеет.

Для того чтобы получить мьютекс, в коде программы следует вызвать метод WaitOne () для этого мьютекса. Метод WaitOne () наследуется классом Mutex от класса Thread. WaitHandle. Ниже приведена его простейшая форма.

Public bool WaitOne ();

Метод WaitOne () ожидает до тех пор, пока не будет получен мьютекс, для которого он был вызван. Следовательно, этот метод блокирует выполнение вызывающего потока до тех пор, пока не станет доступным указанный мьютекс. Он всегда возвращает логическое значение true.