不了解 C# 中任务的行为

sca*_*ell 1 c# task task-parallel-library

我希望有人能帮助我理解一些看似简单的代码。显然,我错过了一些东西。关于这段代码的行为,我不明白两件事:

  1. 如果我让代码运行,则Debug.WriteLine“结束延迟”的最后一个永远不会被写入。为什么是这样?

  2. Task.WaitAll()如果我在 Main() 中的行上放置一个断点,我会看到它delayTask的状态为,并且无论我等待多久,都不会发生WaitingToRun“开始延迟” 。但是,一旦我在 Task.WaitAll() 行上按 F10,我就会看到“开始延迟”出现在输出中。为什么线路上的断点似乎阻止了任务的开始?无论我使用或 ,这种行为都不会改变。Debug.WriteLineDoDelay()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}");
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*asH 5

任务delayTask = Task.Run(() => DoDelay(10000));

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

要解决此问题,您应该让您的方法返回一个任务,即async Task DoDelay(int delayMs). 这不会影响方法返回的时间,但返回的任务让您等待整个方法完成。通过使用异步 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}");
}
Run Code Online (Sandbox Code Playgroud)

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