Как IAsyncStateMachine управляет несколькими ожидающими в MethodBuilder? - PullRequest
0 голосов
/ 24 апреля 2018

Я пытаюсь понять, как эти 4 части сочетаются друг с другом:

IAwaiter
IAwaitable
IAsyncMethodBuilder
IAsyncStateMachine

Я не понимаю взаимосвязи между IAsyncMethodBuilder ожидающим (ями) и конечным автоматом.Если мой методист получает, скажем, 2 ожидающих, почему у машины состояний есть только один ожидающий, на котором он использует результат Get / Set?Я говорю об ожидающих на методологе:

var a= await MyMethod(); 
var b=await MyMethod(); 

Где MyMethod Определение:

async  Task<T> MyMethod(){
await f1()   ->don't care about thsese
await f2()   -----/-----
await f3()   -----/------
........
}

Я выложу два фрагмента кода изБлог Диксина https://weblogs.asp.net/dixin/functional-csharp-asynchronous-function:

Пользовательский код:

internal static async Task<T> Async<T>(T value)
{
    T value1 = Start(value);
    T result1 = await Async1(value1);
    T value2 = Continuation1(result1);
    T result2 = await Async2(value2);
    T value3 = Continuation2(result2);
    T result3 = await Async3(value3);
    T result = Continuation3(result3);
    return result;
}

internal static T Start<T>(T value) => value;

internal static Task<T> Async1<T>(T value) => Task.Run(() => value);

internal static T Continuation1<T>(T value) => value;

internal static Task<T> Async2<T>(T value) => Task.FromResult(value);

internal static T Continuation2<T>(T value) => value;

internal static Task<T> Async3<T>(T value) => Task.Run(() => value);

internal static T Continuation3<T>(T value) => value;

Что генерирует компилятор:

[CompilerGenerated]
[StructLayout(LayoutKind.Auto)]
private struct AsyncStateMachine<TResult> : IAsyncStateMachine
{
    public int State;

    public AsyncTaskMethodBuilder<TResult> Builder;

    public TResult Value;

    private TaskAwaiter<TResult> awaiter;  //Why only one?

    void IAsyncStateMachine.MoveNext()
    {
        TResult result;
        try
        {
            switch (this.State)
            {
                case -1: // Start code from the beginning to the 1st await.
                    // Workflow begins.
                    TResult value1 = Start(this.Value);
                    this.awaiter = Async1(value1).GetAwaiter();
                    if (this.awaiter.IsCompleted)
                    {
                        // If the task returned by Async1 is already completed, immediately execute the continuation.
                        goto case 0;
                    }
                    else
                    {
                        this.State = 0;
                        // If the task returned by Async1 is not completed, specify the continuation as its callback.
                        this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
                        // Later when the task returned by Async1 is completed, it calls back MoveNext, where State is 0.
                        return;
                    }
                case 0: // Continuation code from after the 1st await to the 2nd await.
                    // The task returned by Async1 is completed. The result is available immediately through GetResult.
                    TResult result1 = this.awaiter.GetResult();
                    TResult value2 = Continuation1(result1);
                    this.awaiter = Async2(value2).GetAwaiter();
                    if (this.awaiter.IsCompleted)
                    {
                        // If the task returned by Async2 is already completed, immediately execute the continuation.
                        goto case 1;
                    }
                    else
                    {
                        this.State = 1;
                        // If the task returned by Async2 is not completed, specify the continuation as its callback.
                        this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
                        // Later when the task returned by Async2 is completed, it calls back MoveNext, where State is 1.
                        return;
                    }
                case 1: // Continuation code from after the 2nd await to the 3rd await.
                    // The task returned by Async2 is completed. The result is available immediately through GetResult.
                    TResult result2 = this.awaiter.GetResult();
                    TResult value3 = Continuation2(result2);
                    this.awaiter = Async3(value3).GetAwaiter();
                    if (this.awaiter.IsCompleted)
                    {
                        // If the task returned by Async3 is already completed, immediately execute the continuation.
                        goto case 2;
                    }
                    else
                    {
                        this.State = 2;
                        // If the task returned by Async3 is not completed, specify the continuation as its callback.
                        this.Builder.AwaitUnsafeOnCompleted(ref this.awaiter, ref this);
                        // Later when the task returned by Async3 is completed, it calls back MoveNext, where State is 1.
                        return;
                    }
                case 2: // Continuation code from after the 3rd await to the end.
                    // The task returned by Async3 is completed. The result is available immediately through GetResult.
                    TResult result3 = this.awaiter.GetResult();
                    result = Continuation3(result3);
                    this.State = -2; // -2 means end.
                    this.Builder.SetResult(result);
                    // Workflow ends.
                    return;
            }
        }
        catch (Exception exception)
        {
            this.State = -2; // -2 means end.
            this.Builder.SetException(exception);
        }
    }

    [DebuggerHidden]
    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine asyncStateMachine) =>
        this.Builder.SetStateMachine(asyncStateMachine);
}

Разве у AsyncStateMachine не должно быть списка ожидающих, приведенных в начале моего примера? Если у меня есть N ожидающих в построителе методов, как машина передает SetResult всем ожидающим?

1 Ответ

0 голосов
/ 24 апреля 2018

Конечный автомат может одновременно ожидать только одну ожидаемую операцию.Может быть задействовано несколько операций, но это может быть только в одной точке ожидания одновременно.Поэтому, если эти ожидающие имеют один и тот же тип, для его ожидания требуется только одно поле.

Если в одном и том же методе есть разные типы ожидающих, я думаю, вы увидите одно поле для каждого типа ожидающего.(Компилятор потенциально может использовать одно поле object для всех ожидающих и соответствующим образом преобразовывать его при запуске продолжения, но это вызывает другие проблемы, особенно если ожидающий является типом значения.)

Вот пример:

using System;
using System.Threading.Tasks;

class Test
{
    static async Task FooAsync()
    {
        await Bar<int>();
        await Bar<string>();
        await Task.Delay(1000);
        await Bar<string>();
        await Task.Yield();
    }

    static Task<T> Bar<T>() => Task.FromResult(default(T));
}

Здесь мы получаем конечные поля в автомате:

TaskAwaiter<int> <>u__1;            // From the Bar<int> call
TaskAwaiter<string> <>u__2;         // From both Bar<string> calls
TaskAwaiter <>u__3;                 // From Task.Delay
YieldAwaitable.YieldAwaiter <>u__4; // From Task.Yield
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...