Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.pdf
Скачиваний:
65
Добавлен:
11.05.2015
Размер:
1.81 Mб
Скачать

После того как задача task1 (предшественник) завершается, отказывает или отменяется, задача task2 (продолжение) запускается. Аргумент t, переданный лямбда-выражению продолжения – это ссылка на предшествующую задачу1.

Выполнение продолжения можно запланировать на основе завершения множества предшествующих задач при помощи статических методов

Task.WhenAll() и Task.WhenAny().

var task1 = Task.Run(() => Console.Write("X")); var task2 = Task.Run(() => Console.Write("Y")); var continuation = Task.WhenAll(task1, task2)

.ContinueWith(t => Console.Write("Done"));

Второй способ организации продолжения заключается в использовании объекта ожидания. Объект ожидания – это любой объект, имеющий методы

OnCompleted() и GetResult() и булево свойство IsCompleted. Вызов метода

GetAwaiter() на задаче возвращает объект ожидания. Метод OnCompleted() принимает в качестве аргумента делегат, содержащий код продолжения. Метод GetResult() возвращает результат работы предшественника (или void).

//задача подсчёта простых чисел (используется LINQ)

Task<int> prime = Task.Run(() => Enumerable.Range(2, 3000000 - 2).Count(n =>

Enumerable.Range(2, (int) Math.Sqrt(n) - 1).All(i => n%i > 0)));

//получаем объект продолжения

var awaiter = prime.GetAwaiter();

// указываем, что делать после окончания предшественника awaiter.OnCompleted(() =>

{

// получаем результат вычислений предшественника int result = awaiter.GetResult(); Console.WriteLine(result);

});

33.4. Асинхронные функции в C#

Для поддержки асинхронного программирования в версии C# 5.0 появилась операция await. Синтаксис этой операции следующий:

var результат = await выражение; оператор(ы);

Компилятор разворачивает приведённую выше конструкцию в такой функциональный эквивалент:

1 По умолчанию предшественник и продолжение могут выполняться в разных потоках. Чтобы они выполнялись в одном потоке, передайте методу ContinueWith() дополнительный аргу-

мент со значением TaskContinuationOptions.ExecuteSynchronously.

144

var awaiter = выражение.GetAwaiter(); awaiter.OnCompleted(()=>

{

var результат = awaiter.GetResult();

оператор(ы);

}

Операция await применима и к выражению, не возвращающему значения:

await выражение; оператор(ы);

Выражение, на котором применяется await, обычно является задачей. Тем не менее, компилятор удовлетворит любой объект ожидания (определение данного понятия приведено выше). Операция await может применяться только внутри метода (или лямбда-выражения) со специальным модификатором async. Метод должен возвращать void либо тип Task или Task<TResult>.

Методы с модификатором async называются асинхронными функциями. Встретив выражение await, процесс выполнения (обычно) производит возврат в вызывающий код – почти как оператор yield return в итераторе. Но перед возвратом исполняющая среда присоединяет к ожидающей задаче признак продолжения, гарантирующий, что когда задача завершается, управление перейдёт обратно в метод и продолжится с места, в котором оно его оставило.

Рассмотрим несколько примеров использования операции await. Пусть имеется обычный метод, выполняющий чтение содержимого сайта. Для этого ис-

пользуется класс System.Net.WebClient и его метод DownloadString().

private void ReadFromWeb()

{

var web = new WebClient();

var text = web.DownloadString("http://msdn.com"); Console.WriteLine(text.Length);

}

Вызов метода ReadFromWeb() задержит основной поток выполнения на несколько миллисекунд (или даже секунд – зависит от скорости сети). К счастью,

создатели класса WebClient включили в него метод DownloadStringTaskAsync().

Этот метод создаёт задачу Task<string> для чтения сайта и возвращает управление. Данный факт позволяет нам превратить метод ReadFromWeb() в асинхронную функцию, которая не блокирует при вызове основной поток.

private async void ReadFromWeb()

{

var web = new WebClient();

var text = await web.DownloadStringTaskAsync("http://msdn.com"); Console.WriteLine(text.Length);

}

145