不了解 C# 中任务的行为



我希望有人能帮助我理解什么看起来像一些简单的代码。很明显,我漏掉了什么。关于这段代码的行为,有两件事我不明白:

  1. 如果我让代码运行,'End Delay'的最后一个Debug.WriteLine永远不会被写入。为什么会这样?

  2. 如果我在Main()中的Task.WaitAll()行上放置一个断点,我看到delayTask具有WaitingToRun的状态,并且DoDelay()中的"开始延迟"Debug.WriteLine不会发生,无论我等待多长时间。然而,当我在Task.WaitAll()行上按下F10键时,我看到"Begin Delay"出现在输出中。为什么Task.WaitAll()行的断点似乎阻止开始这个任务吗?无论我使用Task.WaitAll(delayTask)还是await delayTask,这个行为都不会改变。

我正在运行。net Framework 4.6.2,不幸的是,我没有选择。

感谢所有发帖的人。我对上面的第一个问题有一个答案-我把'async void DoDelay()'改为'async Task DoDelay()'。但是对于第二个问题中的行为,我仍然没有任何解释。

更新:正如JonasH向我指出的那样,在VS.

中,所有任务在中断模式下都会停止。对于感兴趣的人来说,这篇文章对理解正在发生的事情有很大帮助:https://www.pluralsight.com/guides/understand-control-flow-async-await

static void Main(string[] args) 
{
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(() => DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask);
}
static async void DoDelay(int delayMs) 
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}

Task delayTask = Task. run (() =>

DoDelay (10000));

这将在后台线程上调用DoDelay(10000)方法,并在方法返回时返回一个完成的任务。然而,DoDelay将返回第一个await语句。该方法的其余部分将在稍后的时间运行,但是没有任何东西将等待此操作的发生。

要解决这个问题,你应该让你的方法返回一个任务,即async Task DoDelay(int delayMs)。这不会影响方法返回的时间,但是返回的任务允许您等待整个方法完成。这可以通过使用async main方法变得更简单:
static async Task Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
var delayTask = DoDelay(10000);
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
await delayTask;
Debug.WriteLine($"Main-C: {DateTime.Now:mm:ss.fff}");
}
static async Task DoDelay(int delayMs) {
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}

只有在你绝对需要的时候才使用async void,即在UI中使用事件处理程序,然后确保你正在处理任何等待任务的失败。在绝大多数情况下,使用async Taskasync Task<T>

注意:避免使用async void因为你必须await所以我把它改成async Task

必须使用delayTask.Wait();等待任务完成。

:delayTask.Wait()是不好的做法,使用await delayTask;代替。

用这个代替你的代码,我做了一些改变:

static void Main(string[] args) {
Debug.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => await DoDelay(10000));
Debug.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
delayTask.Wait();
}
static async Task DoDelay(int delayMs)
{
Debug.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Debug.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}

也可以用

static async Task Main(string[] args) 

和删除Task.Run(async () => await DoDelay(10000));和使用await DoDelay(10000)await delayTask;,但它可能无法工作在。net框架4.6.2

在这种情况下async/await模式的正确实现如下面的代码

static class Program
{
static async Task Main(string[] args)
{
Console.WriteLine($"Main-A: {DateTime.Now:mm:ss.fff}");
Task delayTask = Task.Run(async () => { await DoDelay(10000); });
Task delayTaskSync = Task.Run(() => { DoDelaySync(10000); });
Console.WriteLine($"Main-B: {DateTime.Now:mm:ss.fff}");
Task.WaitAll(delayTask, delayTaskSync);
}
static async Task DoDelay(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
await Task.Delay(delayMs);
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
static void DoDelaySync(int delayMs)
{
Console.WriteLine($"Begin Delay: {DateTime.Now:mm:ss.fff}");
Task.Delay(delayMs).Wait();
Console.WriteLine($"End Delay: {DateTime.Now:mm:ss.fff}");
}
}

这里你可以看到async函数和sync函数的实现。Task.Run()运行一个并行任务,在该任务中,您可以使用async/await的更多任务或使用同步功能。

最新更新