c# - 在没有 async 和 await 的情况下实现 async 和 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() 阻塞了我的线程,因此它们不是让三个调用异步运行,而是一个接一个地按顺序运行。

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

回答1

请注意,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 https://devblogs.microsoft.com/premier-developer/dissecting-the-async-methods-in-c/。所以我真的不确定这个例子应该教什么。

从您的链接中考虑一个稍微简化的示例:

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。因此,无论是否先完成鸡蛋或吐司,它都会尽可能地继续进行准备工作。请注意,这只是为了演示概念,它缺少线程安全之类的东西。

正如您可能看到的那样,幕后工作相当复杂,甚至没有涉及到像 https://hamidmosalla.com/2018/06/24/what-is-synchronizationcontext/ 这样的事情。

回答2

阻塞的不是 Task.Delay(x),而是 Task.Delay(x).Wait()https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.wait?view=net-6.0#system-threading-tasks-task-wait 将同步阻塞。

相反,我会使用 https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.continuewith?view=net-6.0 链接 Task.Delay 任务的延续,以便 onReady() 在任务完成时异步执行,即:

Task.Delay(3000).Wait();

...应该变成:

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

相似文章

uml - 如何处理 SysML/UML 中的不同实现?

想象一下,我们正在构建一个图书馆系统。我们的用例可能是借书查书管理会员资格想象一下,我们可以通过图书管理员或机器来完成这些用例。我们需要实现这些用例。我们应该为不同的流程绘制不同的usecase实现吗...

git - 如何在不保留提交日志的情况下推送到远程

我有以下git分支。B1B2从B1,我创建了分支S1并开始处理它,并提交了一堆更改。稍后,我将更改从B2合并到S1。在此之后,S1中也添加了很多提交。现在我想将更改推送到远程S1。中间有很多不需要的提...

最新文章