实现async和await而不使用async和await



对于一些教程,我计划使用一些回调来解释asyncawait。基于https://learn.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?

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

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

Task.Delay(3000).Wait();

…应该成为:

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

注意任务。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如何将方法重写为状态机。所以我真的不确定这个例子应该教什么。

从你的链接考虑一个稍微简化的例子:

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这样的东西。

最新更新