Tipos de tarefas assíncronas em C #Async Task Types in C#

Estenda async para oferecer suporte a tipos de tarefas que correspondam a um padrão específico, além dos tipos bem conhecidos System.Threading.Tasks.Task e 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>.

Tipo de TarefaTask Type

Um tipo de tarefa é um class ou struct com um tipo de Construtor associado identificado com System.Runtime.CompilerServices.AsyncMethodBuilderAttribute .A task type is a class or struct with an associated builder type identified with System.Runtime.CompilerServices.AsyncMethodBuilderAttribute. O tipo de tarefa pode ser não genérico, para métodos assíncronos que não retornam um valor, ou genérico, para métodos que retornam um valor.The task type may be non-generic, for async methods that do not return a value, or generic, for methods that return a value.

Para dar suporte await a, o tipo de tarefa deve ter um método acessível correspondente GetAwaiter() que retorna uma instância de um tipo de aguardador (consulte expressões Await 7.7.7.1 do C# ).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);
}

Tipo de ConstrutorBuilder Type

O tipo de Construtor é um class ou struct que corresponde ao tipo de tarefa específico.The builder type is a class or struct that corresponds to the specific task type. O tipo de Construtor pode ter no máximo 1 parâmetro de tipo e não deve ser aninhado em um tipo genérico.The builder type can have at most 1 type parameter and must not be nested in a generic type. O tipo de Construtor tem os public métodos a seguir.The builder type has the following public methods. Para tipos não genéricos de Construtor , SetResult() não tem parâmetros.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; }
}

ExecuçãoExecution

Os tipos acima são usados pelo compilador para gerar o código para o computador de estado de um async método.The types above are used by the compiler to generate the code for the state machine of an async method. (O código gerado é equivalente ao código gerado para métodos assíncronos que retornam Task , Task<T> ou void .(The generated code is equivalent to the code generated for async methods that return Task, Task<T>, or void. A diferença é, para esses tipos bem conhecidos, os tipos de Construtor também são conhecidos pelo compilador.)The difference is, for those well known types, the builder types are also known to the compiler.)

Builder.Create() é invocado para criar uma instância do tipo de Construtor.Builder.Create() is invoked to create an instance of the builder type.

Se o computador de estado for implementado como um struct , o builder.SetStateMachine(stateMachine) será chamado com uma instância em caixa do computador de estado que o construtor pode armazenar em cache, se necessário.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) é invocado para associar o Construtor à instância de máquina de estado gerada pelo compilador.builder.Start(ref stateMachine) is invoked to associate the builder with compiler-generated state machine instance. O construtor deve chamar stateMachine.MoveNext() em Start() ou depois de Start() foi retornado para avançar o computador de estado.The builder must call stateMachine.MoveNext() either in Start() or after Start() has returned to advance the state machine. Depois de Start() retorna, o async método chama builder.Task a tarefa para retornar do método Async.After Start() returns, the async method calls builder.Task for the task to return from the async method.

Cada chamada para stateMachine.MoveNext() irá avançar a máquina de estado.Each call to stateMachine.MoveNext() will advance the state machine. Se a máquina de estado for concluída com êxito, builder.SetResult() será chamada, com o valor de retorno do método, se houver.If the state machine completes successfully, builder.SetResult() is called, with the method return value if any. Se uma exceção for lançada no computador de estado, builder.SetException(exception) será chamado.If an exception is thrown in the state machine, builder.SetException(exception) is called.

Se a máquina de estado atingir uma await expr expressão, expr.GetAwaiter() será invocada.If the state machine reaches an await expr expression, expr.GetAwaiter() is invoked. Se o aguardador implementar ICriticalNotifyCompletion e IsCompleted for false, o computador de estado será invocado 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() deve chamar awaiter.OnCompleted(action) com uma ação que chama stateMachine.MoveNext() quando o aguardador é concluído.AwaitUnsafeOnCompleted() should call awaiter.OnCompleted(action) with an action that calls stateMachine.MoveNext() when the awaiter completes. Da mesma forma para INotifyCompletion e builder.AwaitOnCompleted() .Similarly for INotifyCompletion and builder.AwaitOnCompleted().

Resolução de sobrecargaOverload Resolution

A resolução de sobrecarga é estendida para reconhecer tipos de tarefa além de Task e Task<T> .Overload resolution is extended to recognize task types in addition to Task and Task<T>.

Um async lambda sem valor de retorno é uma correspondência exata para um parâmetro candidato de sobrecarga de tipo de tarefa não genérica e um async lambda com tipo de retorno T é uma correspondência exata para um parâmetro candidato de sobrecarga de tipo de tarefa genérico.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.

Caso contrário, se um async lambda não for uma correspondência exata para um dos dois parâmetros candidatos de tipos de tarefa , ou uma correspondência exata para ambos, e houver uma conversão implícita de um tipo candidato para o outro, o de candidato vence.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. Caso contrário, avalie recursivamente os tipos A e B em Task1<A> e Task2<B> para obter uma correspondência melhor.Otherwise recursively evaluate the types A and B within Task1<A> and Task2<B> for better match.

Caso contrário, se um async lambda não for uma correspondência exata para um dos dois parâmetros candidatos de tipos de tarefa , mas um candidato for um tipo mais especializado do que o outro, o candidato mais especializado será o vencedor.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.