Страница 410 из 502
<b> Console.Write("тик ");</b>
<b> Monitor.Pulse(this); // разрешить выполнение метода Tock()</b>
<b> Monitor.Wait(this); // ожидать завершения метода Tock()</b>
<b> }</b>
<b> /* Следующий атрибут полностью синхронизирует метод Тоск(). */</b>
<b> [MethodImplAttribute(MethodImplOptions.Synchronized)]</b>
<b> public void Tock(bool ru
<b> if (!ru
<b> Monitor.Pulse(this); // уведомить любые ожидающие потоки</b>
<b> return;</b>
<b> }</b>
<b> Console.WriteLine("так");</b>
<b> Monitor.Pulse(this); // разрешить выполнение метода Tick()</b>
<b> Monitor.Wait(this); // ожидать завершения метода Tick()</b>
<b> }</b>
<b>}</b>
<b>class MyThread {</b>
<b> public Thread Thrd;</b>
<b> TickTock ttOb;</b>
<b> // Сконструировать новый поток.</b>
<b> public MyThread(string name, TickTock tt) {</b>
<b> Thrd = new Thread(this.Run);</b>
<b> ttOb = tt;</b>
<b> Thrd.Name = name;</b>
<b> Thrd.Start();</b>
<b> }</b>
<b> // Начать выполнение нового потока,</b>
<b> void Run() {</b>
<b> if (Thrd.Name == "Tick") {</b>
<b> for (int i = 0; i < 5; i++) ttOb.Tick(true);</b>
<b> ttOb.Tick(false);</b>
<b> }</b>
<b> else {</b>
<b> for (int i = 0; i < 5; i++) ttOb.Tock(true);</b>
<b> ttOb.Tock(false);</b>
<b> }</b>
<b> }</b>
<b>}</b>
<b>class TickingClock {</b>
<b> static void Main() {</b>
<b> TickTock tt = new TickTock();</b>
<b> MyThread mt1 = new MyThread("Tick", tt);</b>
<b> MyThread mt2 = new MyThread("Tock", tt);</b>
<b> mt1.Thrd.Join();</b>
<b> mt2.Thrd.Join();</b>
<b> Console.WriteLine("Часы остановлены");</b>
<b> }</b>
<b>}</b>
Эта версия программы дает такой же результат, как и предыдущая. Синхронизируемый метод не определен в открытом классе и не вызывается для открытого объекта, поэтому применение оператора lock или атрибута MethodlmplAttribute зависит от личных предпочтений. Ведь и тот и другой дает один и тот же результат. Но поскольку ключевое слово lock относится непосредственно к языку С#, то в примерах, приведенных в этой книге, предпочтение отдано именно ему.
----------------------------
ПРИМЕЧАНИЕ
Не применяйте атрибут MethodImplAttribute в открытых классах или экземплярах открытых объектов. Вместо этого пользуйтесь оператором lock, чтобы заблокировать метод для закрытого объекта, как пояснялось ранее.
----------------------------
Применение мьютекса и семафора
В большинстве случаев, когда требуется синхронизация, оказывается достаточно и оператора lock. Тем не менее в некоторых случаях, как, например, при ограничении доступа к общим ресурсам, более удобными оказываются механизмы синхронизации, встроенные в среду .NET Framework. Ниже рассматриваются по порядку два таких механизма: мьютекс и семафор.
Мьютекс представляет собой взаимно исключающий синхронизирующий объект. Это означает, что он может быть получен потоком только по очереди. Мьютекс предназначен для тех ситуаций, в которых общий ресурс может быть одновременно использован только в одном потоке. Допустим, что системный журнал совместно используется в нескольких процессах, но только в одном из них данные могут записываться в файл этого журнала в любой момент времени. Для синхронизации процессов в данной ситуации идеально подходит мьютекс.
Мьютекс поддерживается в классе System.Threading.Mutex. У него имеется несколько конструкторов. Ниже приведены два наиболее употребительных конструктора.
<b>public Mutex()</b>
<b>public Mutex(bool initiallyOwned)</b>
В первой форме конструктора создается мьютекс, которым первоначально никто не владеет. А во второй форме исходным состоянием мьютекса завладевает вызывающий поток, если параметр ini tiallyOwned имеет логическое значение true. В противном случае мьютексом никто не владеет.
Для того чтобы получить мьютекс, в коде программы следует вызвать метод WaitOne() для этого мьютекса. Метод WaitOne() наследуется классом Mutex от класса Thread.WaitHandle. Ниже приведена его простейшая форма.