Типы асинхронных задач в C #Async Task Types in C#

Расширение async для поддержки типов задач , соответствующих определенному шаблону, в дополнение к хорошо известным типам System.Threading.Tasks.Task и System.Threading.Tasks.Task<T> .Extend async to support task types that match a specific pattern, in addition to the well known types System.Threading.Tasks.Task and System.Threading.Tasks.Task<T>.

Тип задачиTask Type

Тип задачиclass или struct с связанным типом построителя , идентифицированным с помощью System.Runtime.CompilerServices.AsyncMethodBuilderAttribute .A task type is a class or struct with an associated builder type identified with System.Runtime.CompilerServices.AsyncMethodBuilderAttribute. Тип задачи может быть не универсальным, для асинхронных методов, которые не возвращают значение или универсальный, для методов, возвращающих значение.The task type may be non-generic, for async methods that do not return a value, or generic, for methods that return a value.

Для поддержки await тип задачи должен иметь соответствующий, доступный GetAwaiter() метод, возвращающий экземпляр типа метода ожидания (см. статью C# 7.7.7.1 awaited Expressions ).To support await, the task type must have a corresponding, accessible GetAwaiter() method that returns an instance of an awaiter type (see C# 7.7.7.1 Awaitable expressions ).

[AsyncMethodBuilder(typeof(MyTaskMethodBuilder<>))]
class MyTask<T>
{
    public Awaiter<T> GetAwaiter();
}

class Awaiter<T> : INotifyCompletion
{
    public bool IsCompleted { get; }
    public T GetResult();
    public void OnCompleted(Action completion);
}

Тип построителяBuilder Type

Тип построителяclass или struct , соответствующий определенному типу задачи.The builder type is a class or struct that corresponds to the specific task type. Тип построителя может иметь не более 1 параметра типа и не должен быть вложен в универсальный тип.The builder type can have at most 1 type parameter and must not be nested in a generic type. Тип построителя имеет следующие public методы.The builder type has the following public methods. Для типов построителя , не являющихся универсальными, SetResult() параметр не имеет параметров.For non-generic builder types , SetResult() has no parameters.

class MyTaskMethodBuilder<T>
{
    public static MyTaskMethodBuilder<T> Create();

    public void Start<TStateMachine>(ref TStateMachine stateMachine)
        where TStateMachine : IAsyncStateMachine;

    public void SetStateMachine(IAsyncStateMachine stateMachine);
    public void SetException(Exception exception);
    public void SetResult(T result);

    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine;
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine;

    public MyTask<T> Task { get; }
}

ВыполнениеExecution

Приведенные выше типы используются компилятором для создания кода конечного автомата async метода.The types above are used by the compiler to generate the code for the state machine of an async method. (Созданный код эквивалентен коду, созданному для асинхронных методов, которые возвращают Task , Task<T> или void .(The generated code is equivalent to the code generated for async methods that return Task, Task<T>, or void. Разница заключается в том, что для этих хорошо известных типов типы построителя также известны компилятору.)The difference is, for those well known types, the builder types are also known to the compiler.)

Builder.Create() вызывается для создания экземпляра типа построителя.Builder.Create() is invoked to create an instance of the builder type.

Если конечный автомат реализуется как struct , то builder.SetStateMachine(stateMachine) вызывается с упакованным экземпляром конечного автомата, который построитель может кэшировать при необходимости.If the state machine is implemented as a struct, then builder.SetStateMachine(stateMachine) is called with a boxed instance of the state machine that the builder can cache if necessary.

builder.Start(ref stateMachine) вызывается для связывания построителя с экземпляром конечного автомата, созданным компилятором.builder.Start(ref stateMachine) is invoked to associate the builder with compiler-generated state machine instance. Построитель должен вызвать stateMachine.MoveNext() либо в, Start() либо после, Start() чтобы переместить конечный автомат.The builder must call stateMachine.MoveNext() either in Start() or after Start() has returned to advance the state machine. После Start() возврата async метод вызывает, builder.Task чтобы задача возвращалась из асинхронного метода.After Start() returns, the async method calls builder.Task for the task to return from the async method.

Каждый вызов метода stateMachine.MoveNext() переместит конечный автомат.Each call to stateMachine.MoveNext() will advance the state machine. Если конечный автомат завершается успешно, builder.SetResult() вызывается метод с возвращаемым значением метода при его наличии.If the state machine completes successfully, builder.SetResult() is called, with the method return value if any. При возникновении исключения в конечный автомат builder.SetException(exception) вызывается.If an exception is thrown in the state machine, builder.SetException(exception) is called.

Если конечный автомат достигает await expr выражения, expr.GetAwaiter() вызывается метод.If the state machine reaches an await expr expression, expr.GetAwaiter() is invoked. Если ожидающий метод реализует ICriticalNotifyCompletion и IsCompleted имеет значение false, конечный автомат вызывает builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine) .If the awaiter implements ICriticalNotifyCompletion and IsCompleted is false, the state machine invokes builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine). AwaitUnsafeOnCompleted() должен вызываться awaiter.OnCompleted(action) с действием, которое вызывает, stateMachine.MoveNext() когда завершается выполнение метода await.AwaitUnsafeOnCompleted() should call awaiter.OnCompleted(action) with an action that calls stateMachine.MoveNext() when the awaiter completes. Аналогично для INotifyCompletion и builder.AwaitOnCompleted() .Similarly for INotifyCompletion and builder.AwaitOnCompleted().

Overload ResolutionOverload Resolution

Разрешение перегрузки расширено для распознавания типов задач в дополнение к Task и Task<T> .Overload resolution is extended to recognize task types in addition to Task and Task<T>.

asyncЛямбда-выражение без возвращаемого значения является точным соответствием для параметра-кандидата перегрузки неуниверсального типа задачи , а async лямбда с возвращаемым типом T является точным соответствием для параметра кандидата перегрузки универсального типа задачи.An async lambda with no return value is an exact match for an overload candidate parameter of non-generic task type , and an async lambda with return type T is an exact match for an overload candidate parameter of generic task type.

В противном случае, если async лямбда-выражение не является точным соответствием для одного из двух потенциальных параметров типов задач или точное совпадение для обоих, и существует неявное преобразование из одного типа кандидата в другой, от кандидата WINS.Otherwise if an async lambda is not an exact match for either of two candidate parameters of task types , or an exact match for both, and there is an implicit conversion from one candidate type to the other, the from candidate wins. В противном случае рекурсивно вычисляют типы A и B в Task1<A> и Task2<B> для лучшего соответствия.Otherwise recursively evaluate the types A and B within Task1<A> and Task2<B> for better match.

В противном случае, если async лямбда-выражение не является точным соответствием для одного из двух потенциальных параметров типов задач , но один кандидат является более специализированным типом, чем другой, более специализированным кандидатом является WINS.Otherwise if an async lambda is not an exact match for either of two candidate parameters of task types , but one candidate is a more specialized type than the other, the more specialized candidate wins.