Что такое Microsoft.NET?

         

Уведомление с помощью мониторов



Уведомление с помощью мониторов

Поток, овладевший замком монитора Monitor (Монитор), может, не покидая блока синхронизации, ожидать сигнал от другого потока, который выполняет операцию синхронизации на том же самом объекте. Поток вызывает метод Monitor::Wait (Монитор::Ожидать) и освобождает замок. Потом, когда он получит уведомление от другого потока, то повторно овладевает замком в целях синхронизации.
Поток, овладевший замком монитора Monitor (Монитор), может послать уведомление другому потоку, ожидающему разрешения на доступ к тому же самому объекту с помощью методов Pulse (Импульс, Сигнал) или PulseAll. Важно, что во время посылки сигнала поток переходит в состояние ожидания. В противном случае, если сигнал будет послан, но поток не подождет некоторое время, то другой поток будет ждать вечно и никогда не сможет получить уведомление. Такое поведение не похоже на событие возврата в исходное состояние, обсуждаемое позже в этой главе. Если много потоков ожидают сигнал, метод Pulse (Импульс, Сигнал) поместит только один поток в очередь готовых для выполнения. PulseAll поместит их все в эту очередь.
Поток, пославший импульс, больше не владеет замком монитора Monitor (Монитор), но и не блокируется; он может продолжать выполнение. Так как он больше не заблокирован, но не владеет замком, то, чтобы избежать взаимоблокировки (дедлока) или состояния гонок, этот поток должен пробовать повторно овладеть замком (с помощью методов Monitor: :Enter (Монитор::Войти) или Wait (Ожидать)), перед выполнением любых потенциально опасных действий.
Пример PulseAll иллюстрирует применение методов Pulse (Импульс, Сигнал) и PulseAll. Пример во время выполнения генерирует следующую выдачу:

First thread: 2 started.
Thread: 5 started.
Thread: 5 waiting.
Thread: 6 started.
Thread: 6 waiting.
Thread 5 sleeping.
Done .
Thread 5 awake.
Thread: 5 exited.
Thread 6 sleeping.
Thread 6 awake.
Thread: 6 exited.

Пере вод такой:

Первый поток: 2 стартовал.
Поток: 5 стартовал.
Поток: 5 в состоянии ожидания.
Поток: 6 стартовал.
Поток: 6 в состоянии ожидания.
Поток 5 бездействует.
Сделано.
Поток 5 активный.
Поток: 5 вышел.
Поток 6 бездействует.
Поток 6 активный.
Поток: 6 вышел.

Класс X содержит поле "о" типа Ob j ect (Объект), которое будет использоваться в качестве синхронизирующего замка.
Класс также содержит метод Test (Испытание), который используется в качестве делегата потока. Метод овладевает синхронизирующим замком, а затем ждет уведомления. Когда он получит уведомление, то бездействует в течение половины секунды, а затем вновь освобождает замок.
Метод main (главный) создает два потока, которые используют метод X: :Test (Х::Испытание) в качестве делегата своего потока и совместно используют тот же самый объект, предназначенный для синхронизации. Затем он бездействует в течение 2 секунд, чтобы позволить потокам произвести запросы ожидания и освободить замки. Потом метод main (главный) вызывает метод PulseAll, чтобы уведомить оба ожидающих потока и освободить замки. В конечном счете, каждый поток повторно овладевает замком, выводит сообщение на консоль, и в последний раз освобождает замок.

_gc class X
// класс сборщика мусора X
{ private: // частный
Object *o; // Объект public:
X(Object *o) // Объект
{
this->o = о;
}
void Test () // Испытание
{
try
{
long threadld =
"Thread->GetHashCode () ; // Поток
console::WriteLine(
"Thread: {0} sta.ted.",
threadld.ToString()); // "Поток: {0} стартовал."
Monitor::Enter(о1, // Монитор::Войти
Console : : Write1" jre (
'"In.-'ad: tO) waiting.",
threadld.ToString()); // "Поток: {0} ожидает."
Monitor::Wait(о); // Монитор::Ждет
Console::WriteLine( "Thread {0} sleeping.",
threadld.ToString()); // "Поток {0} бездействует."
Thread::Sleep(500); // Поток::Бездействовать
Console::WriteLine( "Thread {0} awake.",
threadld.ToString()); // "Поток {0} активный. "
Monitor::Exit (о); // Выход
Console::WriteLine( "Thread: {0} exited.",
threadld.ToString()); // "Поток: {0} вышел."
}
catch(Exception *e) // Исключение
{
long threadld =
Thread::CurrentThread->GetHashCode();
Console::WriteLine(
"Thread: {0} Exception: {!}", // "Поток: {0} Исключение: {1} "
threadld.ToString(), e->Message); // Сообщение Monitor::Exit(о); // Выход
}
}
};
_gc class Classl
// класс сборщика мусора Classl
{
public:
static Object *o = new Object;
// статический Объект *о = новый Объект;
static void Main()
{
Console::WriteLine(
"First thread: {0} started.",
// "Первый поток: {0} стартовал. ",
Thread::CurrentThread->GetHashCode().ToString() ) ;
X *a = new X(o); X *b = new X(o);
ThreadStart *ats = new ThreadStart(a, X::Test); // Испытание
ThreadStart *bts = new ThreadStart(b, X::Test); // Испытание
Thread *at = new Thread(ats); // новый Поток
Thread *bt = new Thread(bts); // новый Поток
at->Start(); // Начало bt->Start (); // Начало
// Бездействовать, чтобы позволить другим потокам ждать
// объект перед Импульсом (Pulse) Thread::Sleep(2000);
// Бездействие Monitor::Enter (о);
// Монитор::Войти
Monitor::PulseAll(о);
//Monitor::Pulse(о);
Monitor::Exit(о);
// Выход
Console::WriteLine("Done.");
// Сделано
}
};

Только один поток сможет завершить свою работу, если закомментировать вызов PulseAll и убрать комментарий с вызова метода Pulse (Импульс, Сигнал), потому что другие потоки никогда не смогут стать в очередь готовых потоков. Если удалить Sleep (2000) из главной подпрограммы main (главная), то другие потоки заблокируются навсегда, потому что посылка сигнала происходит до того, как потоки получат шанс вызвать метод Wait (Ожидать), и, следовательно, они никогда не получат уведомления. Методы Wait (Ожидать), Pulse (Импульс, Сигнал) и PulseAll могут использоваться для координирования использования несколькими потоками синхронизационных замков.
Метод Thread: : Sleep (Поток::Режим ожидания) приводит к приостановке выполнения текущего потока на указанный период времени. Вызов Thread: : Suspend (Поток::Приостановить) блокирует выполнение потока до вызова Thread: :Resume (Поток::Продолжить) другим потоком. Поток также может быть заблокирован, если он ожидает завершения другого потока (Thread: : Join (Поток::Объединить)). Этот метод использовался в примерах Threading (Организация поточной обработки) так, чтобы главный поток мог ждать завершения выполнения запросов резервирования. Поток может также блокироваться при ожидании синхронизирующего замка (в критической секции).
Вызов Thread: : Interrupt (Поток::Прерывание) для заблокированного потока приводит к его пробуждению. Поток получит ThreadlnterruptedException. И если он не перехватывает это исключение, то среда времени выполнения это исключение перехватит и уничтожит поток.
Если, в качестве последней надежды, нужно уничтожить поток напрямую, необходимо для этого потока сделать вызов метода Thread: :Abort (Поток:прекратить). Метод Thread: :Abort (Поток::Прекратить) приводит к запуску исключения Thread-AbortException. Это исключение не может быть перехвачено, но оно приведет к выполнению всех блоков finally (наконец). Кроме того, Thread: :Abort (Поток:: Прекратить) не приводит к пробуждению ожидающего потока.
Поскольку для выполнения блоков finally (наконец) может потребоваться время, или потоки могут находиться в состоянии ожидания, то потоки, выполнение которых прекращается, могут быть завершены не сразу. Поэтому, если нужно убедиться в том, что выполнение потока завершено, необходимо ожидать завершения потока, вызвав метод Thread: : Join (Поток::Объединить).



Содержание раздела