Страница 425 из 502
<b> tsk2.Start();</b>
<b> Console.WriteLine("Идентификатор задачи tsk: " + tsk.Id);</b>
<b> Console.WriteLine("Идентификатор задачи tsk2: " + tsk2.Id);</b>
<b> // Сохранить метод Main() активным до завершения остальных задач,</b>
<b> for(int i = 0; i < 60; i++) {</b>
<b> Console.Write(".");</b>
<b> Thread.Sleep (100);</b>
<b> }</b>
<b> Console.WriteLine("Основной поток завершен.");</b>
<b> }</b>
<b>}</b>
Выполнение этой программы приводит к следующему результату.
<b>Основной поток запущен.</b>
<b>Идентификатор задачи tsk: 1</b>
<b>Идентификатор задачи tsk2: 2</b>
<b>.MyTask() №1 запущен</b>
<b>MyTask() №2 запущен</b>
<b>....В методе MyTask() #1, подсчет равен 0</b>
<b>В методе MyTask() #2, подсчет равен 0</b>
<b>.....В методе MyTask() #1, подсчет равен 1</b>
<b>В методе MyTask() #2, подсчет равен 1</b>
<b>.....В методе MyTask() #1, подсчет равен 2</b>
<b>В методе MyTask() #2, подсчет равен 2</b>
<b>....В методе MyTask() #2, подсчет равен 3</b>
<b>В методе MyTask() #1, подсчет равен 3</b>
<b>.....В методе MyTask() #1, подсчет равен 4</b>
<b>В методе MyTask() #2, подсчет равен 4</b>
<b>.....В методе MyTask() #1, подсчет равен 5</b>
<b>В методе MyTask() #2, подсчет равен 5</b>
<b>.....В методе MyTask() #1, подсчет равен 6</b>
<b>В методе MyTask() #2, подсчет равен 6</b>
<b>....В методе MyTask() #1, подсчет равен 7</b>
<b>В методе MyTask() #2, подсчет равен 7</b>
<b>.....В методе MyTask() #1, подсчет равен 8</b>
<b>В методе MyTask() #2, подсчет равен 8</b>
<b>.....В методе MyTask() #2, подсчет равен 9</b>
<b>MyTask №2 завершен</b>
<b>В методе MyTask() #1, подсчет равен 9</b>
<b>MyTask №1 завершен</b>
<b>............Основной поток завершен.</b>
Применение методов ожидания
В приведенных выше примерах основной поток исполнения, а по существу, метод Main(), завершался потому, что такой результат гарантировали вызовы метода Thread.Sleep(). Но подобный подход нельзя считать удовлетворительным.
Организовать ожидание завершения задач можно и более совершенным способом, применяя методы ожидания, специально предоставляемые в классе Task. Самым простым из них считается метод Wait(), приостанавливающий исполнение вызывающего потока до тех пор, пока не завершится вызываемая задача. Ниже приведена простейшая форма объявления этого метода.
<b>public void Wait()</b>
При выполнении этого метода могут быть сгенерированы два исключения. Первым из них является исключение ObjectDisposedException. Оно генерируется в том случае, если задача освобождена посредством вызова метода Dispose(). А второе исключение, AggregateException, генерируется в том случае, если задача сама генерирует исключение или же отменяется. Как правило, отслеживается и обрабатывается именно это исключение. В связи с тем что задача может сгенерировать не одно исключение, если, например, у нее имеются порожденные задачи, все подобные исключения собираются в единое исключение типа AggregateException. Для того чтобы выяснить, что же произошло на самом деле, достаточно проанализировать внутренние исключения, связанные с этим совокупным исключением. А до тех пор в приведенных далее примерах любые исключения, генерируемые задачами, будут обрабатываться во время выполнения.
Ниже приведен вариант предыдущей программы, измененный с целью продемонстрировать применение метода Wait() на практике. Этот метод используется внутри метода Main(), чтобы приостановить его выполнение до тех пор, пока не завершатся обе задачи tsk и tsk2.
<b>// Применить метод Wait().</b>
<b>using System;</b>
<b>using System.Threading;</b>
<b>using System.Threading.Tasks;</b>
<b>class DemoTask {</b>
<b> // Метод, исполняемый как задача,</b>
<b> static void MyTask() {</b>
<b> Console.WriteLine("MyTask() №" + Task.CurrentId + " запущен");</b>
<b> for(int count = 0; count < 10; count++) {</b>
<b> Thread.Sleep(500);</b>
<b> Console.WriteLine("В методе MyTask() #" + Task.CurrentId +</b>
<b> ", подсчет равен " + count );</b>
<b> }</b>