AsyncStatus
Started
Completed, Canceled, Error
IAsyncInfo
Id (UInt32)
Status (AsyncStatus)
ErrorCode (Exception)
Cancel()
Close()
IAsyncAction
Completed (delegate)
GetResults (void)
IAsyncActionWithProgress
Completed (delegate)
GetResults (void)
Progress (delegate)
IAsyncOperation
Completed (delegate)
GetResults (TResult)
IAsyncOperationWithProgress
Completed (delegate)
GetResults (TResult)
Progress (delegate)
рис. 25.2.
.Интерфейсы.WinRT,.относящиеся.к.выполнению.асинхронного.
ввода-вывода.и.вычислительных.операций
В .NET Framework для упрощения асинхронных операций используются типы
из пространства имен
System.Threading.Tasks
. Типы для выполнения асин-
хронных вычислений и их использование рассматриваются в главе 27, а типы для
выполнения асинхронного ввода-вывода — в главе 28. Кроме того, в C# имеются
ключевые слова
async
и
await
, которые позволяют выполнять асинхронные опе-
рации с применением модели последовательного программирования, существенно
упрощающей структуру кода.
Следующий код представляет собой переработанную версию упоминавшегося
ранее метода
WinRTAsyncIntro
. В этой версии задействованы некоторые методы
708
Глава.25 .Взаимодействие.с.компонентами.WinRT
расширения .NET Framework, преобразующие модель асинхронного программи-
рования WinRT в более удобную модель программирования C#.
using System; // Необходимо для методов расширения
// из WindowsRuntimeSystemExtensions
...
public async void WinRTAsyncIntro() {
try {
StorageFile file = await KnownFolders.MusicLibrary.GetFileAsync("Song.mp3");
/* Завершение... */
}
catch (OperationCanceledException) { /* Отмена... */ }
catch (SomeOtherException ex) { /* Ошибка... */ }
}
Оператор C#
await
заставляет компилятор провести поиск метода
GetAwaiter
в интерфейсе
IAsyncOperation
, возвращенном методом
GetFileAsync
.
Интерфейс не предоставляет метод
GetAwaiter
, поэтому компилятор ищет метод
расширения. К счастью, разработчик .NET Framework включили в библиотеку
System Runtime WindowsRuntime dll
методы расширения, вызываемые для интер-
фейсов WinRT
IAsyncXxx
.
namespace System {
public static class WindowsRuntimeSystemExtensions {
public static TaskAwaiter GetAwaiter(this IAsyncAction source);
public static TaskAwaiter GetAwaiter(this
IAsyncActionWithProgress source);
public static TaskAwaiter GetAwaiter(this
IAsyncOperation source);
public static TaskAwaiter GetAwaiter(
this IAsyncOperationWithProgress source);
}
}
Во внутренней реализации все эти методы создают объект
TaskCompletionSource
и приказывают объекту
IAsyncXxx
обратиться к методу обратного вызова, который
задает финальное состояние
TaskCompletionSource
при завершении асинхронной
операции. Объект
TaskAwaiter
, возвращаемый методами расширения — то, что
в конечном счете должен получить C#. При завершении асинхронной операции
объект
TaskAwaiter
следит за тем, чтобы код продолжал выполняться через
объект
SynchronizationContext
(см. главу 28), связанный с исходным потоком.
Затем поток выполняет код, сгенерированный компилятором C#, который за-
прашивает свойство
Result
объекта
TaskCompletionSource.Task
; это приводит
к получению результата (
StorageFile
в моем примере), выдаче исключения
OperationCanceledException
в случае отмены или другого исключения в случае
ошибки. Пример внутренней реализации этих методов приведен в конце раздела.
Мы рассмотрели наиболее типичный сценарий вызова асинхронной функции
WinRT API и определения результата. Однако в своем коде я показал, как узнать
об отмене операции, но не объяснил, как на практике производится отмена. Также
709
Проекции.уровня. NET.Framework
остался нерассмотренным вопрос обработки оповещений о ходе выполнения опе-
рации. Чтобы правильно обработать отмену и оповещения, вместо автоматического
вызова компилятором одного из методов расширения
GetAwaiter
следует явно
вызвать один из методов расширения
AsTask
, также явно определяемых классом
WindowsRuntimeSystemExtensions
.
namespace System {
public static class WindowsRuntimeSystemExtensions {
public static Task AsTask(this
IAsyncActionWithProgress source,
CancellationToken cancellationToken, IProgress progress);
public static Task AsTask(
this IAsyncOperationWithProgress source,
CancellationToken cancellationToken, IProgress progress);
// Более простые перегруженные версии не показаны
}
}
Итак, пора рассмотреть реализацию в целом. Вот как происходит асинхронный
вызов функций WinRT API с полной поддержкой отмены и оповещений о ходе
выполнения в тех случаях, когда они необходимы:
using S
ystem; // Для AsTask из WindowsRuntimeSystemExtensions
using System.Threading; // Для CancellationTokenSource
internal sealed class MyClass {
private CancellationTokenSource m_cts = new CancellationTokenSource();
// ВНИМАНИЕ: при вызове из потока графического интерфейса
// весь код выполняется в этом потоке:
private async void MappingWinRTAsyncToDotNet(WinRTType someWinRTObj) {
try {
// Предполагается, что XxxAsync возвращает
// IAsyncOperationWithProgress
IBuffer result = await someWinRTObj.XxxAsync(...)
.AsTask(m_cts.Token, new Progress(ProgressReport));
/* Завершение... */
}
catch (OperationCanceledException) { /* Отмена... */ }
catch (SomeOtherException) { /* Ошибка... */ }
}
private void ProgressReport(UInt32 progress) { /* Оповещение... */ }
public void Cancel() { m_cts.Cancel(); } // Вызывается позднее
}
Конечно, многим читателям хотелось бы понять, как методы
AsTask
преобразуют
объект WinRT
IAsyncXxx
в объект .NET Framework
Task
, к которому в конечном
итоге применяется
await
. В следующем коде представлена внутренняя реализация
710
Do'stlaringiz bilan baham: |