不使用 async and await 实现 async and await

Implementing async and await without async and await

对于某些教程,我计划使用一些回调来解释 asyncawait。基于 https://docs.microsoft.com/dotnet/csharp/programming-guide/concepts/async 我尝试使用回调实现类似的东西:

static void Main(string[] args)
{
    Console.WriteLine("Lets start a delicious breakfast");

    FryEggsCallback(2, () =>
    {
        Console.WriteLine("eggs are ready");
    });
    FryBaconCallback(3, () =>
    {
        Console.WriteLine("bacon is ready");
    });
    ToastBreadCallback(2, x =>
    {
        ApplyButter(x);
        ApplyJam(x);
        Console.WriteLine("toast is ready");
    });
    Console.WriteLine("Breakfast is ready!");
}

static void FryEggsCallback(int count, Action onReady) 
{
    Task.Delay(3000).Wait(); // simulate some work that should be done
    onReady();
}
static void FryBaconCallback(int count, Action onReady)
{
    Task.Delay(3000).Wait();
    onReady();
}
static void ToastBreadCallback(int count, Action<Toast> onReady)
{
    Task.Delay(3000).Wait();
    onReady(new Toast());
}

但是 Task.Delay().Wait() 阻塞了我的线程,所以它们不是异步地 运行ning 三个调用,而是一个接一个地 运行 顺序调用。

我将如何使用回调异步实现这三个调用而不是使用 asyncawait

阻塞的不是 Task.Delay(x),而是 Task.Delay(x).Wait()Task.Wait 会同步阻塞。

我会使用 Task.ContinueWithTask.Delay 任务上链接一个延续,以便 onReady() 在任务完成时异步执行,即:

Task.Delay(3000).Wait();

...应该变成:

Task.Delay(3000).ContinueWith(x => onReady());

请注意,Task.Delay 只是定时器对象的包装器。因此,如果想在任务完成时进行回调,您的示例基本上可以归结为:

new Timer (o => Console.WriteLine("eggs are ready"), null, 3000, Timeout.Infinite);
new Timer (o => Console.WriteLine("bacon is ready"), null, 3000, Timeout.Infinite);

new Timer (o => {
    var toast = new Toast();
    ApplyButter(toast);
    ApplyJam(toast);
    Console.WriteLine("toast is ready");
}, null, 3000, Timeout.Infinite);
Console.WriteLine("Breakfast is ready!")

一个明显的问题是早餐在任何组成部分之前准备好。但也许更重要的是,它并没有解释如何 async rewrites the method to a statemachine。所以我真的不确定这个例子应该教什么。

考虑一下您 link:

中的一个稍微简化的示例
Task<Egg> eggsTask = FryEggsAsync(2);
Task<Toast> toastTask = ToastBreadAsync(2);

Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");

Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");

Console.WriteLine("Breakfast is ready!");

为了模拟这种行为,我们可以使用一个状态机来跟踪我们在准备工作中进行了多远,以及我们拥有哪些组件:

public Egg FriedEgg;
public Toast ToastedBread;
private int state = 0;

public void Run(){
    switch(state){
        case 0;
        if(ToastedBread != null){
            ApplyButter(ToastedBread )   
            ApplyJam(ToastedBread );
            Console.WriteLine("Toast is ready");
            Juice oj = PourOJ();
            Console.WriteLine("Oj is ready");
            state = 1;
            Run(); // Continue to the next state if possible
        }
        break;
       case 1:
       if( FriedEgg != null){
          Console.WriteLine("Eggs are ready");
            state = 2;
            Run(); // Continue to the next state if possible
       }
       break;
       case 2:
           Console.WriteLine("Breakfast is ready!");
           state = 3;
       break;
    }
}

每个计时器回调都会设置相应的 Egg 或 Toast 字段并调用 Run。因此,无论鸡蛋或烤面包是否先完成,它都会尽可能地继续准备工作。请注意,这只是为了演示概念,它缺少诸如线程安全之类的东西。

正如您可能看到的那样,幕后工作相当复杂,甚至没有涉及 synchronizationContexts