| Класс | Описание |
|---|---|
| Task | Представляет асинхронную операцию. |
| Task <TResult> | Представляет асинхронную операцию, которая возвращает значение. |
| TaskScheduler | Объект, обрабатывающий низкоуровневую постановку задач в очередь на потоки. |
| TaskFactory | Для создания объекта Task. |
| TaskFactory <TResult> | Для создания объекта Task, с возвращаемым значением. |
Как уже писалось в лекции №6 задачи можно создавать различными способами. Один из таких способов - это использование лямда-выражения и метода Task.Factory.StartNew():
Task.Factory.StartNew (() => Console.WriteLine ("Hello World!"));
(рис 7.1) Результат выполнения программы с использованием класса Task
Аналогично, можно было вывести на экран сообщение "Hello World!", вначале создав объект Task, а затем вызвав метод Start():
Task task = new Task (() => Console.Write ("Hello World!"));
task.Start();
Что бы выполнить задачу синхронно (в том же потоке) небходимо вызвать метод RunSynchronously() вместо вызова метода Start():
Task task = new Task (() => Console.Write ("Hello World!"));
task.RunSynchronously()
Класс Task <TResult>, позволяет получить определенный набор данных после завершения выполнения задачи:
Task<int> task = Task.Factory.StartNew< int > (()
{
int i,c,j;
c=5;
j=5;
i=c+j;
return i;
});
DoSomething(); // Выполнение другой операции асинхронно
Console.WriteLine("Результат выполнения задачи:"+task.Result);
Console.ReadLine();
(рис 7.2) Результат выполнения программы с использованием класса Task <TResult>
Вложенные задача - это экземпляр задачи Task, создаваемый в пользовательском делегате другой задачи. Дочерняя задача - это вложенная задача, создаваемая с помощью параметра AttachedToParent. Задача может создать любое количество дочерних и вложенных задач, ограничиваясь только системными ресурсами. Пример кода, демонстрирующий создание вложенных задач, представлен ниже:
Task parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Родительская задача запущена");
Task.Factory.StartNew(() => // Дочерняя задача
{
Console.WriteLine("Дочерняя задача запущена");
Thread.SpinWait(5000000);
Console.WriteLine("Дочерняя задача выполнена");
});
});
parent.Wait();
Console.WriteLine("Родительская задача выполнена");
(рис 7.3) Результат выполнения программы с использованием дочерних задач
Наиболее важным моментом при сравнении дочерних и вложенных задач является то, что вложенные задачи по существу не зависят от родительской или внешней задачи, тогда как вложенные дочерние задачи синхронизируются с родительской задачей. Если изменить оператор создания задач так, чтобы использовался параметр AttachedToParent, как показано в следующем примере:
Task parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Родительская задача запущена");
Task.Factory.StartNew(() => // Дочерняя задача
{
Console.WriteLine("Дочерняя задача запущена");
Thread.SpinWait(5000000);
Console.WriteLine("Дочерняя задача выполнена");
}, TaskCreationOptions.AttachedToParent);
});
parent.Wait();
Console.WriteLine("Родительская задача выполнена");
(рис 7.4) Результат выполнения программы с использованием дочерних задач и параметра AttachedToParent
Возможно, осуществлять ожидание выполнения задач двумя различными способами:
Wait(). Один из возможных форматов объявления этого метода представлен ниже:
public Wait(TimeSpan timeout)
где timeout - время ожидания в миллисекундах.
Result (свойство класса Task<TResult>):
public TResult Result;
Можно также одновременно ожидать завершения нескольких задач с помощью метода Task.WaitAll() который ожидает завершения всех указанных задач и метода Task.WaitAny() который ожидает завершения какой-либо задачи. Форматы объявления этих методов представлены ниже:
Public static void WaitAll(Task[]); Public static void WaitAny(Task[]);
где Task[] - массив экземпляров класса Task.
Метод WaitAll() ожидает выполнение всех остальных задач, по завершению работы задач вызывает исключение типа AggregateException, которое содержит все исключения неудачно завершившихся задач.
Вызов метода WaitAny() эквивалентен ожиданию ManualResetEventSlim, который переводится в сигнальное состояние при завершении конкретной задачи.
При ожидании завершения задачи с помощью метода Wait(), или свойства Result, любое необработанное исключение будет передано вызывающему коду, а именно объекту AggregationException, что делает не обязательным обработку исключений внутри самой задачи:
int x = 0;
Task<int> z = Task.Factory.StartNew (() => 10 / x);
try
{
Console.WriteLine (calc.Result);
}
catch (AggregateException aex)
{
Console.Write (aex.InnerException.Message);
}
Но все же необходимо обрабатывать исключения автономных задач (т.е задач, которые не являются родительскими для других задач, и завершения, выполнения которых никто не ожидает).
(рис 7.5) Результат выполнения программы с обработкой исключения
Отмена выполнения заданий осуществляется с помощью специальных маркеров отмены (token). Структура CancellationToken - распространяет уведомление о том, что операции следует отменить. При запуске задачи можно передать маркер отмены, что позволит отменить выполнение задачи:
CancellationTokenSource cancelSource = new CancellationTokenSource();
CancellationToken token = cancelSource.Token;
Task task = Task.Factory.StartNew (() =>
{
token.ThrowIfCancellationRequested(); // Проверяем запрос отмены
}, token);
...
cancelSource.Cancel();
Для отмененной задачи вызывается исключение - AggregateException, которое необходимо обработать и проверить следующим образом:
try
{
task.Wait();
}
catch (AggregateException ex)
{
if (ex.InnerException is OperationCanceledException)
Console.Write ("Задание отменено");
}
(рис 7.6) Результат выполнения программы с использованием отмены задачи
Для того что бы упорядочить выполнение задач в программе, т.е. к примеру что бы Задача 3 выполнялась сразу же после выполнения Задачи 2, а Задача 2 после завершения Задачи 1, необходимо использовать метод ContinueWith() класса - Task. Форма объявление данного метода представлена ниже:
public Task ContinueWith(Action<Task>)
Пример работы метода ContinueWith() представлен ниже:
Task task1 = Task.Factory.StartNew (() => Console.Write ("Hello "));
Task task2 = task1.ContinueWith (main => Console.Write ("World!"));
где main - аргумент, который передается в задачу-продолжение в лямбда-выражении, является ссылкой на родительскую задачу.
(рис 7.7) Результат выполнения программы с использование метода ContinueWith()
В классе Task<TResult>, также можно использовать продолжение и возвращать некоторые данные, с использованием последовательности вычисления:
Task.Factory.StartNew<int>(() => 10)
.ContinueWith(main => main.Result * 2)
.ContinueWith(main => Math.Sqrt(main.Result))
.ContinueWith(main => Console.WriteLine(main.Result));
(рис 7.8) Результат выполнения программы с использование продолжения в классе Task<TResult>
Продолжение может определить, было ли сгенерировано исключение родительской задачей, с помощью свойства Exception родительской задачи. Следующий фрагмент кода будет выводить подробную информацию об исключении NullReferenceException:
Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (main => Console.Write (main.Exception));
(рис 7.9) Результат выполнения программы с использование продолжения и обработки исключения NullReferenceException
Одной из ключевых возможностей использования продолжения является то, что оно будет выполняться только после завершения дочерних задач. При этом любое исключение, генерируемое дочерней задачей, будет передаваться в задачу-продолжение:
TaskCreationOptions main = TaskCreationOptions.AttachedToParent;
Task.Factory.StartNew (() =>
{
Task.Factory.StartNew (() => { throw null; }, main);
Task.Factory.StartNew (() => { throw null; }, main);
Task.Factory.StartNew (() => { throw null; }, main);
})
.ContinueWith (p => Console.WriteLine (p.Exception),
TaskContinuationOptions.OnlyOnFaulted);
(рис 7.10) Результат выполнения программы с использование продолжения и дочерних задач
Метод ContinueWhenAll() используется для того, что бы выполнить продолжение конкретной задачи, после выполнения нескольких предыдущих задач:
Task<int> task1 = Task.Factory.StartNew (() => 200);
Task<int> task2 = Task.Factory.StartNew (() => 200);
Task<int> task3 = Task<int>.Factory.ContinueWhenAll (
new[] { task1, task2 }, tasks => tasks.Sum (t => t.Result));
Console.WriteLine (task3.Result);
(рис 7.11) Результат выполнения программы с использование метода Метод ContinueWhenAll()
Метод ContinueWith() позволяет создавать несколько продолжений одной задачи. Когда предыдущая задача завершается, все продолжения запускаются одновременно. Пример использования метода ContinueWith() представлен ниже:
var t = Task.Factory.StartNew (() => Thread.Sleep (1000));
t.ContinueWith (main => Console.Write ("X"));
t.ContinueWith (main => Console.Write ("Y"));
(рис 7.12) Результат выполнения программы с использование метода ContinueWith()
Планировщик задач (task scheduler) назначает задания определенным потокам. Все задания связаны с определенным планировщиком, который представлен абстрактным классом TaskScheduler. .Net Framework предоставляет две конкретные реализации:
Что бы в фоновом режиме получить данные от Web-сервиса и затем, на основе полученных результатов, обновить метку (label) с именем lblResult. Разбиваем эту задачу на две подзадачи:
Если указать для задачи-продолжения планировщик контекста синхронизации, полученный при создании окна, можно обновить lblResult:
public partial class MyWindow : Window
{
TaskScheduler _uiScheduler; // Объявляем TaskScheduler
public MyWindow()
{
InitializeComponent();
_uiScheduler = TaskScheduler.
FromCurrentSynchronizationContext();
Task.Factory.StartNew<string> (SomeComplexWebService)
.ContinueWith (main => lblResult.Content = main.Result, _uiScheduler);
}
string SomeComplexWebService() { ... }
}
| Класс | Описание |
|---|---|
| Task | Представляет асинхронную операцию. |
| Task <TResult> | Представляет асинхронную операцию, которая возвращает значение. |
| TaskScheduler | Объект, обрабатывающий низкоуровневую постановку задач в очередь на потоки. |
| TaskFactory | Для создания объекта Task. |
| TaskFactory <TResult> | Для создания объекта Task, с возвращаемым значением. |
Как уже писалось в лекции №6 задачи можно создавать различными способами. Один из таких способов - это использование лямда-выражения и метода Task.Factory.StartNew():
Task.Factory.StartNew (() => Console.WriteLine ("Hello World!"));
(рис 7.1) Результат выполнения программы с использованием класса Task
Аналогично, можно было вывести на экран сообщение "Hello World!", вначале создав объект Task, а затем вызвав метод Start():
Task task = new Task (() => Console.Write ("Hello World!"));
task.Start();
Что бы выполнить задачу синхронно (в том же потоке) небходимо вызвать метод RunSynchronously() вместо вызова метода Start():
Task task = new Task (() => Console.Write ("Hello World!"));
task.RunSynchronously()
Класс Task <TResult>, позволяет получить определенный набор данных после завершения выполнения задачи:
Task<int> task = Task.Factory.StartNew< int > (()
{
int i,c,j;
c=5;
j=5;
i=c+j;
return i;
});
DoSomething(); // Выполнение другой операции асинхронно
Console.WriteLine("Результат выполнения задачи:"+task.Result);
Console.ReadLine();
(рис 7.2) Результат выполнения программы с использованием класса Task <TResult>
Вложенные задача - это экземпляр задачи Task, создаваемый в пользовательском делегате другой задачи. Дочерняя задача - это вложенная задача, создаваемая с помощью параметра AttachedToParent. Задача может создать любое количество дочерних и вложенных задач, ограничиваясь только системными ресурсами. Пример кода, демонстрирующий создание вложенных задач, представлен ниже:
Task parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Родительская задача запущена");
Task.Factory.StartNew(() => // Дочерняя задача
{
Console.WriteLine("Дочерняя задача запущена");
Thread.SpinWait(5000000);
Console.WriteLine("Дочерняя задача выполнена");
});
});
parent.Wait();
Console.WriteLine("Родительская задача выполнена");
(рис 7.3) Результат выполнения программы с использованием дочерних задач
Наиболее важным моментом при сравнении дочерних и вложенных задач является то, что вложенные задачи по существу не зависят от родительской или внешней задачи, тогда как вложенные дочерние задачи синхронизируются с родительской задачей. Если изменить оператор создания задач так, чтобы использовался параметр AttachedToParent, как показано в следующем примере:
Task parent = Task.Factory.StartNew(() =>
{
Console.WriteLine("Родительская задача запущена");
Task.Factory.StartNew(() => // Дочерняя задача
{
Console.WriteLine("Дочерняя задача запущена");
Thread.SpinWait(5000000);
Console.WriteLine("Дочерняя задача выполнена");
}, TaskCreationOptions.AttachedToParent);
});
parent.Wait();
Console.WriteLine("Родительская задача выполнена");
(рис 7.4) Результат выполнения программы с использованием дочерних задач и параметра AttachedToParent
Возможно, осуществлять ожидание выполнения задач двумя различными способами:
Wait(). Один из возможных форматов объявления этого метода представлен ниже:
public Wait(TimeSpan timeout)
где timeout - время ожидания в миллисекундах.
Result (свойство класса Task<TResult>):
public TResult Result;
Можно также одновременно ожидать завершения нескольких задач с помощью метода Task.WaitAll() который ожидает завершения всех указанных задач и метода Task.WaitAny() который ожидает завершения какой-либо задачи. Форматы объявления этих методов представлены ниже:
Public static void WaitAll(Task[]); Public static void WaitAny(Task[]);
где Task[] - массив экземпляров класса Task.
Метод WaitAll() ожидает выполнение всех остальных задач, по завершению работы задач вызывает исключение типа AggregateException, которое содержит все исключения неудачно завершившихся задач.
Вызов метода WaitAny() эквивалентен ожиданию ManualResetEventSlim, который переводится в сигнальное состояние при завершении конкретной задачи.
При ожидании завершения задачи с помощью метода Wait(), или свойства Result, любое необработанное исключение будет передано вызывающему коду, а именно объекту AggregationException, что делает не обязательным обработку исключений внутри самой задачи:
int x = 0;
Task<int> z = Task.Factory.StartNew (() => 10 / x);
try
{
Console.WriteLine (calc.Result);
}
catch (AggregateException aex)
{
Console.Write (aex.InnerException.Message);
}
Но все же необходимо обрабатывать исключения автономных задач (т.е задач, которые не являются родительскими для других задач, и завершения, выполнения которых никто не ожидает).
(рис 7.5) Результат выполнения программы с обработкой исключения
Отмена выполнения заданий осуществляется с помощью специальных маркеров отмены (token). Структура CancellationToken - распространяет уведомление о том, что операции следует отменить. При запуске задачи можно передать маркер отмены, что позволит отменить выполнение задачи:
CancellationTokenSource cancelSource = new CancellationTokenSource();
CancellationToken token = cancelSource.Token;
Task task = Task.Factory.StartNew (() =>
{
token.ThrowIfCancellationRequested(); // Проверяем запрос отмены
}, token);
...
cancelSource.Cancel();
Для отмененной задачи вызывается исключение - AggregateException, которое необходимо обработать и проверить следующим образом:
try
{
task.Wait();
}
catch (AggregateException ex)
{
if (ex.InnerException is OperationCanceledException)
Console.Write ("Задание отменено");
}
(рис 7.6) Результат выполнения программы с использованием отмены задачи
Для того что бы упорядочить выполнение задач в программе, т.е. к примеру что бы Задача 3 выполнялась сразу же после выполнения Задачи 2, а Задача 2 после завершения Задачи 1, необходимо использовать метод ContinueWith() класса - Task. Форма объявление данного метода представлена ниже:
public Task ContinueWith(Action<Task>)
Пример работы метода ContinueWith() представлен ниже:
Task task1 = Task.Factory.StartNew (() => Console.Write ("Hello "));
Task task2 = task1.ContinueWith (main => Console.Write ("World!"));
где main - аргумент, который передается в задачу-продолжение в лямбда-выражении, является ссылкой на родительскую задачу.
(рис 7.7) Результат выполнения программы с использование метода ContinueWith()
В классе Task<TResult>, также можно использовать продолжение и возвращать некоторые данные, с использованием последовательности вычисления:
Task.Factory.StartNew<int>(() => 10)
.ContinueWith(main => main.Result * 2)
.ContinueWith(main => Math.Sqrt(main.Result))
.ContinueWith(main => Console.WriteLine(main.Result));
(рис 7.8) Результат выполнения программы с использование продолжения в классе Task<TResult>
Продолжение может определить, было ли сгенерировано исключение родительской задачей, с помощью свойства Exception родительской задачи. Следующий фрагмент кода будет выводить подробную информацию об исключении NullReferenceException:
Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (main => Console.Write (main.Exception));
(рис 7.9) Результат выполнения программы с использование продолжения и обработки исключения NullReferenceException
Одной из ключевых возможностей использования продолжения является то, что оно будет выполняться только после завершения дочерних задач. При этом любое исключение, генерируемое дочерней задачей, будет передаваться в задачу-продолжение:
TaskCreationOptions main = TaskCreationOptions.AttachedToParent;
Task.Factory.StartNew (() =>
{
Task.Factory.StartNew (() => { throw null; }, main);
Task.Factory.StartNew (() => { throw null; }, main);
Task.Factory.StartNew (() => { throw null; }, main);
})
.ContinueWith (p => Console.WriteLine (p.Exception),
TaskContinuationOptions.OnlyOnFaulted);
(рис 7.10) Результат выполнения программы с использование продолжения и дочерних задач
Метод ContinueWhenAll() используется для того, что бы выполнить продолжение конкретной задачи, после выполнения нескольких предыдущих задач:
Task<int> task1 = Task.Factory.StartNew (() => 200);
Task<int> task2 = Task.Factory.StartNew (() => 200);
Task<int> task3 = Task<int>.Factory.ContinueWhenAll (
new[] { task1, task2 }, tasks => tasks.Sum (t => t.Result));
Console.WriteLine (task3.Result);
(рис 7.11) Результат выполнения программы с использование метода Метод ContinueWhenAll()
Метод ContinueWith() позволяет создавать несколько продолжений одной задачи. Когда предыдущая задача завершается, все продолжения запускаются одновременно. Пример использования метода ContinueWith() представлен ниже:
var t = Task.Factory.StartNew (() => Thread.Sleep (1000));
t.ContinueWith (main => Console.Write ("X"));
t.ContinueWith (main => Console.Write ("Y"));
(рис 7.12) Результат выполнения программы с использование метода ContinueWith()
Планировщик задач (task scheduler) назначает задания определенным потокам. Все задания связаны с определенным планировщиком, который представлен абстрактным классом TaskScheduler. .Net Framework предоставляет две конкретные реализации:
Что бы в фоновом режиме получить данные от Web-сервиса и затем, на основе полученных результатов, обновить метку (label) с именем lblResult. Разбиваем эту задачу на две подзадачи:
Если указать для задачи-продолжения планировщик контекста синхронизации, полученный при создании окна, можно обновить lblResult:
public partial class MyWindow : Window
{
TaskScheduler _uiScheduler; // Объявляем TaskScheduler
public MyWindow()
{
InitializeComponent();
_uiScheduler = TaskScheduler.
FromCurrentSynchronizationContext();
Task.Factory.StartNew<string> (SomeComplexWebService)
.ContinueWith (main => lblResult.Content = main.Result, _uiScheduler);
}
string SomeComplexWebService() { ... }
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.