}
Console.WriteLine(Thrd.Name + " завершен!");
// Уведомить о событии,
mre.Set();
}
}
class ManualEventDemo {
static void Main() {
ManualResetEvent evtObj = new ManualResetEvent(false);
MyThread mt1 = new MyThread("Событийный Поток 1", evtObj);
Console.WriteLine("Основной поток ожидает событие.");
// Ожидать уведомления о событии.
evtObj.WaitOne();
Console.WriteLine("Основной поток получил " +
"уведомление о событии от первого потока.");
// Установить событийный объект в исходное состояние.
evtObj.Reset();
mt1 = new MyThread("Событийный Поток 2", evtObj);
// Ожидать уведомления о событии.
evtObj.WaitOne();
Console.WriteLine("Основной поток получил " +
"уведомление о событии от второго потока.");
}
}
Ниже приведен результат выполнения рассматриваемой здесь программы, хотя у вас он может оказаться несколько иным.
Внутри потока Событийный Поток 1
Событийный Поток 1
Основной поток ожидает событие.
Событийный Поток 1
Событийный Поток 1
Событийный Поток 1
Событийный Поток 1
Событийный Поток 1 завершен!
Основной поток получил уведомление о событии от первого потока.
Внутри потока Событийный Поток 2
Событийный Поток 2
Событийный Поток 2
Событийный Поток 2
Событийный Поток 2
Событийный Поток 2
Событийный Поток 2 завершен!
Основной поток получил уведомление о событии от второго потока.
Прежде всего обратите внимание на то, что событие типа ManualResetEvent
передается непосредственно конструктору класса MyThread
. Когда завершается метод Run()
из класса MyThread
, он вызывает для событийного объекта метод Set()
, устанавливающий этот объект в сигнальное состояние. В методе Main()
формируется событийный объект evtObj
типа ManualResetEvent
, первоначально устанавливаемый в исходное, несигнальное состояние. Затем создается экземпляр объекта типа MyThread
, которому передается событийный объект evtObj
. После этого основной поток ожидает уведомления о событии. А поскольку событийный объект evtObj
первоначально находится в несигнальном состоянии, то основной поток вынужден ожидать до тех пор, пока для экземпляра объекта типа MyThread
не будет вызван метод Set()
устанавливающий событийный объект evtObj
в сигнальное состояние. Это дает возможность основному потоку возобновить свое выполнение. Затем событийный объект устанавливается в исходное состояние, и весь процесс повторяется, но на этот раз для второго потока. Если бы не событийный объект, то все потоки выполнялись бы одновременно, а результаты их выполнения оказались бы окончательно запутанными. Для того чтобы убедиться в этом, попробуйте закомментировать вызов метода WaitOne()
в методе Main()
.
Если бы в рассматриваемой здесь программе событийный объект типа AutoResetEvent
использовался вместо событийного объекта типа ManualResetEvent
, то вызывать метод Reset()
в методе Main()
не пришлось бы. Ведь в этом случае событийный объект автоматически устанавливается в несигнальное состояние, когда поток, ожидающий данное событие, возобновляет свое выполнение. Для опробования этой разновидности события замените в данной программе все ссылки на объект типа ManualResetEvent
ссылками на объект типа AutoResetEvent
и удалите все вызовы метода Reset()
. Видоизмененная версия программы будет работать так же, как и прежде.
Еще одним классом, связанным с синхронизацией, является класс Interlocked
. Этот класс служит в качестве альтернативы другим средствам синхронизации, когда требуется только изменить значение общей переменной. Методы, доступные в классе Interlocked
, гарантируют, что их действие будет выполняться как единая, непрерываемая операция. Это означает, что никакой синхронизации в данном случае вообще не требуется. В классе Interlocked
предоставляются статические методы для сложения двух целых значений, инкрементирования и декрементирования целого значения, сравнения и установки значений объекта, обмена объектами и получения 64-разрядно-го значения. Все эти операции выполняются без прерывания.
Читать дальше