为什么Task.Wait()的行为不像Console.WriteLine() – .NET Core那样?

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

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),主线程等待然后终止而没有任何输出.

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

>主UI线程等待/阻止任务直到完成,订阅已完成的任务,因此回调立即执行,输出打印为216816.但这不会发生!

要么

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

那么我在这里想念的是什么,能有人对我有所了解吗?

最佳答案 首先,
according MSDN,你不应该使用OnCompleted方法

This API supports the product infrastructure and is not intended to be used directly from your code.

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

在这种情况下你应该使用的代码是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找到.

点赞