Дополнительные возможности AsyncEnumerator - Поддержка APM

ОГЛАВЛЕНИЕ

Поддержка APM

В моей предыдущей статье я объяснил, как вызов метода Execute AsyncEnumerator начинает исполнение кода итератора. Однако я также объяснил, что поток, вызывающий Execute, будет блокироваться до выхода из итератора или исполнения им оператора yield break.

Блокировка потока может повредить масштабируемости приложения и крайне нежелательна, особенно в серверных приложениях. Она также вредит скорости ответа при вызове потоком графического интерфейса пользователя, поскольку на исполнение итератора уходит неопределенный промежуток времени, и в течение этого времени приложение Windows® Forms или Windows Presentation Foundation (WPF) не будет реагировать на ввод. Вызывать Execute определено стоит только при написании тестового кода или экспериментах с методом итератора.

Для рабочего кода следует вызывать такие методы AsyncEnumerator, как BeginExecute и EndExecute. Внутренне, при вызове BeginExecute, объект AsyncEnumerator конструирует экземпляр класса AsyncResultNoResult, о котором я рассказывал в выпуске журнала MSDN® Magazine за март 2007 года (msdn.microsoft.com/magazine/cc163467). При вызове BeginExecute можно передать ссылку на ваш собственный метод AsyncCallback, и объект AsyncEnumerator вызовет этот метод, когда итератор завершит его исполнение. Затем этот метод должен вызвать метод EndExecute из AsyncEnumerator для получения результатов итератора. Ниже я покажу несколько примеров, в которых я пользуюсь преимуществами методов BeginExecute и EndExecute. Такой метод выглядит следующим образом:

public class AsyncEnumerator<TResult>: AsyncEnumerator {
  public IAsyncResult BeginExecute(
   IEnumerator<Int32> enumerator,
   AsyncCallback callback, Object state);

  public void EndExecute(IAsyncResult result);
}

Также, поскольку AsyncEnumerator поддерживает APM, его можно интегрировать со всеми моделями приложений Microsoft .NET Framework, поскольку они уже поддерживают APM. Это значит, что AsyncEnumerator можно использовать с приложениями веб-форм ASP.NET, веб-службами XML ASP.NET, службами Windows Communication Foundation (WCF), приложениями Windows Forms, приложениями WPF, консольными приложениями, службами Windows и так далее.

Также стоит указать, что поскольку AsyncEnumerator поддерживает APM, его можно использовать внутри другого итератора для компоновки асинхронных операций. Например, можно создать итератор, который знает, как асинхронно выполнить запрос к базе данных и обработать его результат, когда тот появится. Я называю это подпрограммой-итератором. Затем внутри другого итератора можно инициализировать несколько запросов к базе данных, вызвав подпрограмму-итератор в цикле. Для каждой итерации цикла можно создать AsyncEnumerator и вызывать его метод BeginExecute, передавая ему имя подпрограммы-итератора и любые дополнительные аргументы, какие необходимо.

Обратите внимание, что эта модель дает важное преимущество: все подпрограммы-итераторы работают параллельно, не блокируя потоков (если лежащая в основе реализация APM не блокирует потоки, скажем в случае применения BeginXxx для постановки в очередь к пулу потоков ThreadPool делегата, ставящего блок до завершения какой-либо операции). Это позволяет создать простой итератор, включающий в себя одну асинхронную операцию и вызывающий ее изнутри других итераторов, сохраняя при этом масштабируемость и скорость ответа.