【问题标题】:Why does Task.Wait() not behave as expected with Console.WriteLine() - .NET Core?为什么 Task.Wait() 在 Console.WriteLine() - .NET Core 中的行为不符合预期?
【发布时间】:2017-04-23 08:21:30
【问题描述】:

我试图更好地理解异步编程的基础知识,所以我创建了以下 sn-p:

private void TaskContinuations()
{
    // Task for counting prime numbers
    Task<int> primeNumberTask = Task.Run(() =>
        Enumerable.Range(2, 3000000)
        .Count(n =>
            Enumerable.Range(2, (int)Math.Sqrt(n) - 1)
            .All(i => n % i > 0)));

    var awaiter = primeNumberTask.GetAwaiter();

    // primeNumberTask.Wait(); // Option 1: Waiting but not printing (rather unexpected here)

    awaiter.OnCompleted(() => 
    {
        var result = awaiter.GetResult();
        Console.WriteLine(result);
    });

    //primeNumberTask.Wait(); // Option 2: Waiting but not printing (kind of expected here)

    Console.Read(); // Option 3: Works and prints as expected
}

我了解我必须阻止主 UI 任务停止,以防止后台任务被终止。所以Console.Read(); 阻塞了 UI 线程,如果我在质数任务完成之前没有按任何键,控制台中的输出是正确的216816。到这里一切都清楚了。

所以我认为调用 Wait(); 会产生与主 UI 任务阻塞并等待质数任务完成相同的效果(至少如果在 OnCompleted 回调之前调用)。这确实发生了,但在这两种情况下(选项 1 和选项 2),主线程都会等待,然后终止而没有任何输出。

这是为什么呢?我对行为的期望如下(可能有点偏离,因此是我的问题)

  1. 主 UI 线程等待/阻塞任务直到它完成,订阅完成的任务,所以回调立即执行,输出打印为216816。但这不会发生!

  1. 主 UI 线程等待/阻塞,当任务完成时,它会在执行 OnCompleted 之前立即执行此操作。所以这里没有预期的输出。

那么我在这里错过了什么,有人可以解释一下我吗?

【问题讨论】:

  • 当您取消注释 primeNumberTask.Wait(); 时,您是否还有 Console.Read();。如果你不这样做 - 程序在它有机会输出任何东西的那一刻就已经返回了。
  • 不保留 Console.Read();它在所有情况下都会打印“216816”。给出的选项是“独占或”;-)
  • @anytoe 如果选项 2 中没有 Console.Read();,则程序会在触发 .OnCompleted() 之前退出。
  • 这符合我对选项 2 的期望,但选项 1 呢?

标签: .net asynchronous concurrency task-parallel-library .net-core


【解决方案1】:

首先according MSDN,你不应该使用OnCompleted方法,因为

此 API 支持产品基础架构,不打算直接从您的代码中使用

第二件事是OnCompleted 方法正在被调用,嗯,任务完成之后。所以你在这里有一个竞争条件 - 任务完成,.Net 尝试执行它之后的方法,但你的代码中没有任何东西在等待awaiter,所以程序在打印之前退出。

在这种情况下您应该使用的代码是ContinueWith task,等待第二个任务:

private void TaskContinuations()
{
    // Task for counting prime numbers
    var primeNumberTask = Task.Run(() =>
        Enumerable.Range(2, 3000000)
        .Count(n =>
            Enumerable.Range(2, (int)Math.Sqrt(n) - 1)
            .All(i => n % i > 0)));

    // continuation
    var awaiterTask = primeNumberTask.ContinueWith(t => 
    {
        var result = t.Result;
        Console.WriteLine(result);
    });

    // wait for everything to execute
    awaiterTask.Wait();
}

请注意,ContinueWith has it's own pitfalls,应谨慎使用。关于延续技术的另一篇文章可以找到here

【讨论】:

    猜你喜欢
    • 2021-01-05
    • 2012-12-07
    • 1970-01-01
    • 1970-01-01
    • 2019-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-17
    相关资源
    最近更新 更多