Дополнительные возможности AsyncEnumerator - Управление потоком обратного вызова с помощью контекстов синхронизации

ОГЛАВЛЕНИЕ

Управление потоком обратного вызова с помощью контекстов синхронизации

По мере завершения асинхронных операции, различные потоки пула потоков пробуждаются для уведомления объекта AsyncEnumerator. Если AsyncEnumerator использует эти потоки для обратного вызова к итератору, то код итератора может быть исполнен различными потоками, даже если код итератора может исполнять внутри себя не более одного потока. В некоторых ситуациях исполнение кода итератора различными потоками может доставить проблемы. Например, в приложении Windows Forms или WPF элемент управления должен управляться потоком, создавшим его, и это не может быть поток пула потоков.

Исполнение кода итератора через произвольные потоки пула потоков может создать другую проблему. В случае, например, приложения ASP.NET, когда впервые приходит запрос клиента, ASP.NET связывает IPrincipal клиента (для олицетворения), а также информацию о культуре со свойствами CurrentPrincipal, CurrentCulture и CurrentUICulture потока пула потоков. Однако, если использовать этот поток для вызова какого-либо метода BeginXxx, то при исполнении нового потока пула потоков для уведомления пользователя о завершении операции в новом потоке пула потоков эти свойства не будут установлены верно по умолчанию.

Чтобы помочь в решении этих проблем, CLR позволяет каждому потоку иметь связанный с ним объект, производный от SynchronizationContext. Этот поток используется, чтобы помочь в поддержании потоковой модели, применяемой моделью приложений. Для Windows Forms и WPF, их производные от SynchronizationContext объекты знают, как передавать вызов функции (сделанный потоком пула потоков) потоку графического интерфейса пользователя. Что касается ASP.NET, то ее производный от SynchronizationContext объект знает, как инициализировать участника и свойства культуры в каждом потоке пула потоков, используемом для обработки одного запроса.

Для обеспечения работы правильной потоковой модели приложения AsyncEnumerator предлагает свойство SyncContext. Это свойство инициализируется значением, возвращаемым статическим свойством Current («Текущий») контекста SynchronizationContext внутри конструктора AsyncEnumerator. Если оно пустое (как обычно должно быть для консоли или приложения службы Windows), то всякий раз, как поток пула потоков вызывает объект AsyncEnumerator, объект просто использует этот поток для вызова к итератору. Однако, если свойство SyncContext непустое, то объект AsyncEnumerator заставляет поток пула потоков вызвать итератор через указанный объект, производный от SynchronizationContext.

Так что для Windows Forms и приложения WPF это означает, что код итератора всегда будет исполняться через поток графического интерфейса пользователя и, следовательно, для обновления элементов управления на форме можно просто исполнить код в итераторе. Нет нужды вызывать методы элемента управления Invoke/BeginInvoke или методы диспетчера Invoke/BeginInvoke. Это делает несложным возложение на итератор вывода сообщений о ходе работы в интерфейсе пользователя при завершении асинхронных операций. Для ASP.NET это означает, что участник и свойства культуры всегда будут установлены верно при исполнении кода итератора. И код на рис. 2 и пример Windows Forms, который я покажу ниже, пользуются этой функцией.