ASP.NET MVC 4
Адам Фриман
Использование асинхронных методов
Одним из самых серьезных дополнений C# в .NET 4.5 является улучшенный способ работы асинхронных методов. Асинхронные методы скрыты и работают в фоновом режиме, а затем они уведомляют вас, когда завершают свою работу, что позволяет коду выполнять другие задачи, пока идет работа в фоновом режиме. Асинхронные методы являются важным инструментом в устранении узких мест в коде и позволяют приложениям работать параллельно на нескольких процессорах и процессорных ядрах.
C# и. NET отлично поддерживают асинхронные методы, но тогда код становится немного громоздким, и разработчики, которые не привыкли к параллельному программированию, часто вязнут в необычном синтаксисе. В качестве простого примера в листинге 4-32 представлен асинхронный метод, который называется GetPageLength
. Мы определили его в классе MyAsyncMethods
и добавили в папку Models
.
Листинг 4-32: Простой асинхронный метод
using System.Net.Http;
using System.Threading.Tasks;
namespace LanguageFeatures.Models
{
public class MyAsyncMethods
{
public static Task<long?> GetPageLength()
{
HttpClient client = new HttpClient();
var httpTask = client.GetAsync("http://apress.com");
// мы можем здесь делать другие вещи, пока мы ждем
// окончания HTTP запроса
return httpTask.ContinueWith((Task<HttpResponseMessage> antecedent) =>
{
return antecedent.Result.Content.Headers.ContentLength;
});
}
}
}
Это простой метод, который использует объект System.Net.Http.HttpClient
для запроса содержимого страницы Apress и возвращает ее длину. Мы выделили ту часть метода, которая может привести к путанице, что является примером возобновления задачи.
.NET будет работать асинхронно используя Task
. Объекты Task
строго типизированы и основываются на результате, который возвращает работа в фоновом режиме. Так что когда мы вызываем метод HttpClient.GetAsync
, то, что нам вернется, будет Task<HttpResponseMessage>
. Это говорит нам о том, что запрос будет выполняться в фоновом режиме и о том, что результат запроса будет объектом HttpResponseMessage
.
Совет
Когда мы используем такие слова, как фоновый режим, мы опускаем много деталей, чтобы сосредоточиться на ключевых моментах, которые важны для MVC. .NET поддержка асинхронных методов и параллельного программирования в целом превосходна, и мы советуем вам узнать как можно больше об этом, если вы хотите создавать по-настоящему высокопроизводительные приложения, которые могут использовать преимущества многоядерных и многопроцессорных аппаратных средств. Мы вернемся к асинхронным методам MVC в главе 17.
Та часть, в которой могут увязнуть большинство программистов, это возобновление задачи. Это механизм, при помощи которого вы указываете, что должно произойти, когда будет выполнена фоновая задача. В этом примере мы использовали метод ContinueWith
для обработки объекта HttpResponseMessage
который мы получаем от метода HttpClient.GetAsync
. Это мы сделали при помощи лямбда-выражения, которое возвращает значение свойства, возвращающего длину контента, которой мы получили от веб-сервера Apress. Обратите внимание, что мы дважды используем ключевое слово return
:
return httpTask.ContinueWith((Task<HttpResponseMessage> antecedent) => {
return antecedent.Result.Content.Headers.ContentLength;
});
Это та часть, которая доставляет головную боль. Первое использование ключевого слова return
указывает на то, что мы возвращаем объект Task<HttpResponseMessage>
, который после выполнения задачи вернет (будет использовать return
) длину заголовка ContentLength
. Заголовок ContentLength
возвращает результат long?
, и это обозначает, что результат нашего метода GetPageLength
будет Task<long?>
:
public static Task<long?> GetPageLength() {
Не волнуйтесь, если вам это покажется бессмысленным: вы в этом не одиноки, и это очень простой пример. Сложные асинхронные операции могут связывать большое число задач, используя метод ContinueWith
, который создает трудный для чтения и сложный в поддержке код.
Применение ключевых слов async и await
Microsoft представил два новых ключевых слова в C#, которые специально предназначены для упрощения использования асинхронных методов, таких как HttpClient.getAsync
. Новые ключевые слова – это async
и await
, и вы можете увидеть в листинге 4-33, как мы использовали их для упрощения нашего примера.
Листинг 4-33: Использование ключевых словasync
иawait
using System.Net.Http;
using System.Threading.Tasks;
namespace LanguageFeatures.Models
{
public class MyAsyncMethods
{
public async static Task<long?> GetPageLength()
{
HttpClient client = new HttpClient();
var httpMessage = await client.GetAsync("http://apress.com");
// мы можем здесь делать другие вещи, пока мы ждем
// окончания HTTP запроса
return httpMessage.Content.Headers.ContentLength;
}
}
}
Мы использовали ключевое слово await
при вызове асинхронного метода. Это говорит компилятору C#, что мы хотим подождать результат Task
, который возвращает метод GetAsync
, а затем продолжить выполнение других операторов в том же методе.
Применение ключевого слова await
обозначает, что мы можем обрабатывать результат, полученный из метода GetAsync
, как будто это обычный метод, и просто присвоить объект HttpResponseMessage
, который он возвращает, переменной. И что еще лучше, мы можем использовать ключевое слово return
в обычном порядке для получения результата от другого метода, в данном случае, значение свойства ContentLength
. Это гораздо более естественный способ, и это обозначает, что мы не должны беспокоиться о методе ContinueWith
и многократном использовании ключевого слова return
.
При использовании ключевого слова await
необходимо также добавлять ключевое слово async
в описание метода, как мы это сделали в нашем примере. И тип, возвращенный методом, не изменится: наш метод из примера GetPageLength
по-прежнему возвращает Task<long?>
. Это происходит потому, что await
и async
реализованы при помощи некоторых классных уловок компилятора. То есть, они позволяют нам использовать более естественный синтаксис, но они не меняют то, что происходит в методах, к которым они применяются. Кто-то, кто вызывает наш метод GetPageLength
, по-прежнему имеет дело с результатом Task<long?>
, потому что существует фоновое действие, результатом которого является значение long?
. Хотя, конечно, программисты сами выбирают, использовать ли им await
и async
.
Примечание
Возможно, вы заметили, что мы не предоставили MVC примера для тестирования ключевых слов
async
иawait
. Это потому что использование асинхронных методов в MVC контроллерах требует специальных технических навыков. И до главы 17 мы дадим вам еще много полезной информации по этой теме.