当操作未知时如何等待 Task.Run(action)



我有以下代码:

static void Main(string[] args)
{
Run(() => LongProcess()).ContinueWith( t => Run(() => LongerProcess()));
int count = 5;
do
{
Console.WriteLine(count.ToString());
Thread.Sleep(100);
count++;
} while (count < 20);
Console.ReadKey();
}
private static void LongProcess()
{
Console.WriteLine("Long Process 1");
Thread.Sleep(2000);
Console.WriteLine("Long Process 2");
}
private static void LongerProcess()
{
Console.WriteLine("Longer Process 1");
Thread.Sleep(3000);
Console.WriteLine("Longer Process 2");
}
private static async Task Run(Action action)
{
await Task.Run(action);  //shouldn't this wait until the action is completed??
// how should I modify the code so that the program waits until action is done before returning?
}

随着await Task.Run(action);,我预计该程序将等到完成任何action,然后再进行下一步。换句话说,我期待这样的输出:

Long Process 1
Long Process 2
Longer Process 1
Longer Process 2
5
6
7
8
...
19

但是,输出就像

Long Process 1
5
6
7
8
...
19
Long Process 2
Longer Process 1
Longer Process 2

问题:

  1. 为什么await Task.Run不等到action完成?
  2. 如何更改代码以提供我想要的输出?

使用await不会等待,这就是重点。如果要等待,请使用Task.Wait

但是,只有在等待的任务完成后,async方法的执行才会恢复(这是一个延续(。让我们看看您的async方法:

private static async Task Run(Action action)
{
await Task.Run(action);
// There is nothing here
}

任务完成后没有要执行的代码...好的,async方法返回一个任务,该任务完成后完成。让我们看看你是否await...

Run(() => LongProcess()).ContinueWith( t => Run(() => LongerProcess()));

你没有。你启动了那个任务,然后忘记了它。


我认为这就是你想要的:

static async void Main(string[] args)
{
await Run(() => LongProcess());
await Run(() => LongerProcess());
int count = 5;
do
{
Console.WriteLine(count.ToString());
Thread.Sleep(100);
count++;
} while (count < 20);
Console.ReadKey();
}

注意:异步主是 C# 7.1 功能。

此代码将运行调用LongProcess的任务,然后作为延续运行LongerProcess的任务,然后作为延续运行代码的其余部分。

为什么你想要它而不是只是同步调用这些方法超出了我的范围。

顺便说一下,在async方法中,您可以使用await Task.Delay(milliseconds)而不是Thread.Sleep(milliseconds)。优点是线程不会等待。这就像在单个执行计时器上进行延续一样。


如果async/await只是延续,您可能想知道为什么人们想要使用它们而不是ContinueWith

让我给你几个理由:

  • 带有async/await的代码更易于阅读。更少的嵌套内容使代码变得模糊。您可以独立于理解线程来理解代码的作用。
  • 使用async/await编写代码更容易。你只是把它写得好像它是同步的,有一些asyncawait在那里闪闪发光。您可以编写同步代码,然后担心如何使其并发。你最终也会写得更少,这意味着你的工作效率更高。
  • 使用async/await编写代码更智能™。编译器将代码重写为延续状态机,允许您将await语句中放入,而无需任何代码体操™。这延伸到异常处理。你看,在你的触发和忘记任务继续中,你没有处理异常。如果LongerProcess扔怎么办?好吧,有了async/await,您只需将await放入try ... catch中,它就可以做正确的事情™。

这都是很好的关注点分离。


您可能还想知道在什么情况下使用async/await而不是同步代码是个好主意。答案是它在处理 I/O 操作时大放异彩。与外部系统交互时,通常没有立即响应。例如,如果要从磁盘读取或从网络下载。

事实上,如果您考虑操作窗体并将其事件处理为 I/O 绑定操作(因为它们是(。您会发现它们是使用async/await的好地方。特别是考虑到默认情况下,UI 线程的同步上下文将在同一线程上保留延续。当然,您可以通过使用Task.Runasync/await与 CPU 绑定操作一起使用。什么时候是个好主意?啊,是的,当您想保持 UI 响应时。

最新更新