При выполнении этой программы получается следующий результат.
Основной поток запущен Идентификатор задачи tsk: 1 Идентификатор задачи tsk2: 2 MyTask() №1 запущен MyTask() №2 запущен
MyTask №1 завершен
В методе MyTaskO №2, подсчет равен 9
MyTask №2 завершен Основной поток завершен.
Как следует из приведенного выше результата, выполнение метода Main () приостанавливается до тех пор, пока не завершатся обе задачи tsk и tsk2. Следует, однако, иметь в виду, что в рассматриваемой здесь программе последовательность завершения задач tsk и tsk2 не имеет особого значения для вызовов метода Wait (). Так, если первой завершается задача tsk2, то в вызове метода tsk. Wait () будет по-прежнему ожидаться завершение задачи tsk. В таком случае вызов метода tsk2 . Wait () приведет к выполнению и немедленному возврату из него, поскольку задача tsk2 уже завершена.
В данном случае оказывается достаточно двух вызовов метода Wait (), но того же результата можно добиться и более простым способом, воспользовавшись методом Wait АН (). Этот метод организует ожидание завершения группы задач. Возврата из него не произойдет до тех пор, пока не завершатся все задачи. Ниже приведена простейшая форма объявления этого метода.
public static void WaitAll(params Task[]
Задачи, завершения которых требуется ожидать, передаются с помощью параметра в виде массива
Для того чтобы посмотреть, как метод WaitAll () действует на практике, замените в приведенной выше программе следующую последовательность вызовов.
tsk.Wait (); tsk2.Wait ();
на
Task.WaitAll(tskf tsk2);
Программа будет работать точно так же, но логика ее выполнения станет более понятной.
Организуя ожидание завершения нескольких задач, следует быть особенно внимательным, чтобы избежать взаимоблокировок. Так, если две задачи ожидают завершения друг друга, то вызов метода WaitAll () вообще не приведет к возврату из него. Разумеется, условия для взаимоблокировок возникают в результате ошибок программирования, которых следует избегать. Следовательно, если вызов метода WaitAll () не приводит к возврату из него, то следует внимательно проанализировать, могут ли две задачи или больше взаимно блокироваться. (Вызов метода Wait (), который не приводит к возврату из него, также может стать причиной взаимоблокировок.)
Иногда требуется организовать ожидание до тех пор, пока не завершится любая из группы задач. Для этой цели служит метод Wait Ап у (). Ниже приведена простейшая форма его объявления.
public static int WaitAny(params Task[]
Задачи, завершения которых требуется ожидать, передаются с помощью параметра в виде массива
Попробуйте применить метод WaitAny () на практике, подставив в предыдущей программе следующий вызов.
Task.WaitAny(tsk, tsk2);
Теперь, выполнение метода Main () возобновится, а программа завершится, как только завершится одна из двух задач.
Помимо рассматривавшихся здесь форм методов Wait(), WaitAll () и WaitAny (), имеются и другие их варианты, в которых можно указывать период простоя или отслеживать признак отмены. (Подробнее об отмене задач речь пойдет далее в этой главе.)
Вызов метода Dispose ()
В классе Task реализуется интерфейс IDisposable,B котором определяется метод Dispose () . Ниже приведена форма его объявления.
public void Dispose ()
Метод Dispose () реализуется в классе Task, освобождая ресурсы, используемые этим классом. Как правило, ресурсы, связанные с классом Task, освобождаются автоматически во время 'сборки мусора' (или по завершении программы). Но если эти ресурсы требуется освободить еще раньше, то для этой цели служит метод Dispose (). Это особенно важно в тех программах, где создается большое число задач, оставляемых на произвол судьбы.
Следует, однако, иметь в виду, что метод Dispose () можно вызывать для отдельной задачи только после ее завершения. Следовательно, для выяснения факта завершения отдельной задачи, прежде чем вызывать метод Dispose (), потребуется некоторый механизм, например, вызов метода Wait (). Именно поэтому так важно было рассмотреть метод Wait (), перед тем как обсуждать метод Dispose (). Ели же попытаться вызвать Dispose () для все еще активной задачи, то будет сгенерировано исключение InvalidOperationException.
Во всех примерах, приведенных в этой главе, создаются довольно короткие задачи, которые сразу же завершаются, и поэтому применение метода Dispose () в этих примерах не дает никаких преимуществ. (Именно по этой причине вызывать метод Dispose () в приведенных выше программах не
