我有以下代码:
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
问题:
- 为什么
await Task.Run
不等到action
完成? - 如何更改代码以提供我想要的输出?
使用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
编写代码更容易。你只是把它写得好像它是同步的,有一些async
,await
在那里闪闪发光。您可以编写同步代码,然后担心如何使其并发。你最终也会写得更少,这意味着你的工作效率更高。 - 使用
async/await
编写代码更智能™。编译器将代码重写为延续状态机,允许您将await
语句中放入,而无需任何代码体操™。这延伸到异常处理。你看,在你的触发和忘记任务继续中,你没有处理异常。如果LongerProcess
扔怎么办?好吧,有了async/await
,您只需将await
放入try ... catch
中,它就可以做正确的事情™。
这都是很好的关注点分离。
您可能还想知道在什么情况下使用async/await
而不是同步代码是个好主意。答案是它在处理 I/O 操作时大放异彩。与外部系统交互时,通常没有立即响应。例如,如果要从磁盘读取或从网络下载。
事实上,如果您考虑操作窗体并将其事件处理为 I/O 绑定操作(因为它们是(。您会发现它们是使用async/await
的好地方。特别是考虑到默认情况下,UI 线程的同步上下文将在同一线程上保留延续。当然,您可以通过使用Task.Run
将async/await
与 CPU 绑定操作一起使用。什么时候是个好主意?啊,是的,当您想保持 UI 响应时。