Главная страница   /   4.9. Использование асинхронных методов (ASP.NET MVC 4

ASP.NET MVC 4

ASP.NET MVC 4

Адам Фриман

4.9. Использование асинхронных методов

Одним из самых серьезных дополнений 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 мы дадим вам еще много полезной информации по этой теме.