Шрифт:
Интервал:
Закладка:
Попробуйте применить метод 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);
После этого оператора сразу же начнет выполняться метод MyTask().
Метод StartNew() оказывается более эффективным в тех случаях, когда задача создается и сразу же запускается на исполнение. Поэтому именно такой подход и применяется в последующих примерах программ.
Применение лямбда-выражения в качестве задачи
Кроме использования обычного метода в качестве задачи, существует и другой, более рациональный подход: указать лямбда-выражение как отдельно решаемую задачу. Напомним, что лямбда-выражения являются особой формой анонимных функций. Поэтому они могут исполняться как отдельные задачи. Лямбда-выражения оказываются особенно полезными в тех случаях, когда единственным назначением метода является решение одноразовой задачи. Лямбда-выражения могут составлять отдельную задачу или же вызывать другие методы. Так или иначе, применение лямбда-выражения в качестве задачи может стать привлекательной альтернативой именованному методу.
В приведенном ниже примере программы демонстрируется применение лямбда-выражения в качестве задачи. В этой программе код метода MyTask() из предыдущих примеров программ преобразуется в лямбда-выражение.
// Применить лямбда-выражение в качестве задачи.
using System;
using System.Threading;
using System.Threading.Tasks;
class DemoLambdaTask {
static void Main() {
Console.WriteLine("Основной поток запущен.");
// Далее лямбда-выражение используется для определения задачи.
Task tsk = Task.Factory.StartNew(() => {
Console.WriteLine("Задача запущена");
for (int count = 0; count < 10; count++) {
Thread.Sleep(500);
Console.WriteLine("Подсчет в задаче равен " + count );
}
Console.WriteLine("Задача завершена");
} );
// Ожидать завершения задачи tsk.
tsk.Wait();
// Освободить задачу tsk.
tsk.Dispose();
Console.WriteLine("Основной поток завершен.");
}
}
Ниже приведен результат выполнения этой программы.
Основной поток запущен.
Задача запущена
Подсчет в задаче равен 0
Подсчет в задаче равен 1
Подсчет в задаче равен 2
Подсчет в задаче равен 3
Подсчет в задаче равен 4
Подсчет в задаче равен 5
Подсчет в задаче равен 6
Подсчет в задаче равен 7
Подсчет в задаче равен 8
Подсчет в задаче равен 9
Задача завершена
Основной поток завершен.
Помимо применения лямбда-выражения для описания задачи, обратите также внимание в данной программе на то, что вызов метода tsk.Dispose() не делается до тех пор, пока не произойдет возврат из метода tsk.Wait(). Как пояснялось в предыдущем разделе, метод Dispose() можно вызывать только по завершении задачи. Для того чтобы убедиться в этом, попробуйте поставить вызов метода tsk.Dispose() в рассматриваемой здесь программе перед вызовом метода tsk.Wait(). Вы сразу же заметите, что это приведет к исключительной ситуации.
Создание продолжения задачи
Одной из новаторских и очень удобных особенностей библиотеки TPL является возможность создавать продолжение задачи. Продолжение — это одна задача, которая автоматически начинается после завершения другой задачи. Создать продолжение можно, в частности, с помощью метода ContinueWith(), определенного в классе Task. Ниже приведена простейшая форма его объявления:
public Task ContinueWith(Action<Task> действие_продолженмя)
где действие_продолжения обозначает задачу, которая будет запущена на исполнение по завершении вызывающей задачи. У делегата Action имеется единственный параметр типа Task. Следовательно, вариант делегата Action, применяемого в данном методе, выглядит следующим образом.
public delegate void Action<in T>(T obj)
В данном случае обобщенный параметр Т обозначает класс Task.
- QT 4: программирование GUI на С++ - Жасмин Бланшет - Программирование
- C# для профессионалов. Том II - Симон Робинсон - Программирование
- ИНФОРМАЦИОННАЯ ТЕХНОЛОГИЯ. РУКОВОДСТВО ПО УПРАВЛЕНИЮ ДОКУМЕНТИРОВАНИЕМ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ - ГОССТАНДАРТ РОССИИ - Программирование
- Управление исходными текстами. Часть 1. Краткое руководство по CVS - Илья Рыженков - Программирование
- Гибкое управление проектами и продуктами - Борис Вольфсон - Программирование
- Каждому проекту своя методология - Алистэр Коуберн - Программирование
- Разработка ядра Linux - Роберт Лав - Программирование
- Как спроектировать современный сайт - Чои Вин - Программирование
- Творческий отбор. Как создавались лучшие продукты Apple во времена Стива Джобса - Кен Косиенда - Прочая околокомпьтерная литература / Интернет / Программирование
- Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT. Часть 2 - Александр Фролов - Программирование