Иногда требуется организовать ожидание до тех пор, пока не завершится любая из группы задач. Для этой цели служит метод WaitAny(). Ниже приведена простейшая форма его объявления.
public static int WaitAny(params Task[] tasks)
Задачи, завершения которых требуется ожидать, передаются с помощью параметра в виде массива tasks объектов типа Taskили отдельного списка аргументов типа Task. Этот метод возвращает индекс задачи, которая завершается первой. При этом могут быть сгенерированы различные исключения.
Попробуйте применить метод WaitAny()на практике, подставив в предыдущей программе следующий вызов.
Task.WaitAny(tsk, tsk2);
Теперь, выполнение метода Main()возобновится, а программа завершится, как только завершится одна из двух задач.
Помимо рассматривавшихся здесь форм методов Wait(), WaitAll()и WaitAny(), имеются и другие их варианты, в которых можно указывать период простоя или отслеживать признак отмены. (Подробнее об отмене задач речь пойдет далее в этой главе.)
Вызов метода Dispose()
В классе Taskреализуется интерфейс IDisposable, в котором определяется метод Dispose(). Ниже приведена форма его объявления.
public void Dispose()
Метод Dispose()реализуется в классе Task, освобождая ресурсы, используемые этим классом. Как правило, ресурсы, связанные с классом Task, освобождаются автоматически во время "сборки мусора" (или по завершении программы). Но если эти ресурсы требуется освободить еще раньше, то для этой цели служит метод Dispose(). Это особенно важно в тех программах, где создается большое число задач, оставляемых на произвол судьбы.
Следует, однако, иметь в виду, что метод Dispose()можно вызывать для отдельной задачи только после ее завершения. Следовательно, для выяснения факта завершения отдельной задачи, прежде чем вызывать метод Dispose(), потребуется некоторый механизм, например, вызов метода Wait(). Именно поэтому так важно было рассмотреть метод Wait(), перед тем как обсуждать метод Dispose(). Ели же попытаться вызвать Dispose()для все еще активной задачи, то будет сгенерировано исключение InvalidOperationException.
Во всех примерах, приведенных в этой главе, создаются довольно короткие задачи, которые сразу же завершаются, и поэтому применение метода Dispose()в этих примерах не дает никаких преимуществ. (Именно по этой причине вызывать метод Dispose()в приведенных выше программах не было никакой необходимости. Ведь все они завершались, как только завершалась задача, что в конечном итоге приводило к освобождению от остальных задач.) Но в целях демонстрации возможностей данного метода и во избежание каких-либо недоразумений метод Dispose()будет вызываться явным образом при непосредственном обращении с экземплярами объектов типа Taskво всех последующих примерах программ. Если вы обнаружите отсутствие вызовов метода Dispose()в исходном коде, полученном из других источников, то не удивляйтесь этому. Опять же, если программа завершается, как только завершится задача, то вызывать метод Dispose()нет никакого смысла — разве что в целях демонстрации его применения.
Применение класса TaskFactory для запуска задачи
Приведенные выше примеры программы были составлены не так эффективно, как следовало бы, поскольку задачу можно создать и сразу же начать ее исполнение, вызвав метод StartNew(), определенный в классе TaskFactory. В классе TaskFactoryпредоставляются различные методы, упрощающие создание задач и управление ими. По умолчанию объект класса TaskFactoryможет быть получен из свойства Factory, доступного только для чтения в классе Task. Используя это свойство, можно вызвать любые методы класса TaskFactory. Метод StartNew()существует во множестве форм. Ниже приведена самая простая форма его объявления:
public Task StartNew(Action action)
где action — точка входа в исполняемую задачу. Сначала в методе StartNew()автоматически создается экземпляр объекта типа Taskдля действия, определяемого параметром action , а затем планируется запуск задачи на исполнение. Следовательно, необходимость в вызове метода Start()теперь отпадает.
Например, следующий вызов метода StartNew()в рассматривавшихся ранее программах приведет к созданию и запуску задачи tskодним действием.
Task tsk = Task.Factory.StartNew(MyTask);
Читать дальше