在调用 Wait 之前访问任务的结果有什么实际作用?

What does accessing a Result on a Task before calling Wait actually do?

var task = Task.Run(() => DoSomeStuff()).Result;

幕后发生了什么?

我做了一个小测试:

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        var r = Task.Run( () => {Thread.Sleep(5000); return 123; }).Result;
        Console.WriteLine(r);
    }
}

5 秒后打印“123”。那么在 Task 上访问任何此类 属性 是否可以作为调用 Task.Wait() 的快捷方式,即这样做安全吗?


以前我的代码调用 Task.Delay(5000) 立即返回“123”。我在我的问题中解决了这个问题,但把它留在这里作为评论和答案参考。

So does accessing any such property on Task act as a shortcut to calling Task.Wait()?

是的。

来自 the docs:

Accessing the [Result] property's get accessor blocks the calling thread until the asynchronous operation is complete; it is equivalent to calling the Wait method.

但是,您的测试并没有按照您的想法进行。

Task.Delay(..) returns a Task 在指定的时间后完成。它不会阻塞调用线程。

所以() => { Task.Delay(5000); return 123; }只是创建一个新的Task(将在5秒内完成),然后将其丢弃并立即returns 123.

您可以:

  1. 阻塞调用线程,方法是 Task.Delay(5000).Wait()(与 Thread.Sleep(5000) 的作用相同)
  2. 异步等待从 Task.Delay 返回的 Task 完成:Task.Run(async () => { await Task.Delay(5000); return 123; })

测试不会等待 Task.Delay(),所以会立即 returns。应该是:

var r = Task.Run(async  () => { await Task.Delay(5000); return 123; }).Result;

Result 的行为定义明确 - 如果任务尚未完成,它会阻塞直到完成。访问其他任务属性不会阻塞

你问了两个问题。首先,访问 Result 会隐式导致同步 Wait 吗?是的。但更重要的问题是:

is this safe to do?

这样做不安全。

很容易陷入这样一种情况,即您正在同步等待的任务在完成 之前已经安排了将来 到 运行 的工作线程 您刚刚进入休眠状态 。现在我们有这样一种情况,线程在 sleeping 线程做一些工作之前不会醒来,但它永远不会做 因为它睡着了

如果您已经知道任务已完成,那么同步等待结果是安全的。如果不这样做,那么同步等待是不安全

现在,您可能会说,假设我通过其他方式知道同步等待未完成的任务是安全的。那么等待是否安全?好吧,根据问题的假设,是的,但等待仍然聪明。请记住,异步的全部意义在于在高延迟的世界中有效地管理资源。如果您正在同步等待异步任务完成,那么您就是在强迫一个工作人员休眠,直到另一个工作人员完成;睡觉的工人可能正在工作!异步的全部意义在于避免 worker 空闲的情况,所以不要强迫他们。

一个await是一个异步等待。这是一个等待,意思是"wait on running the rest of the current workflow until after this task is done, but find something to do while you are waiting"。我们做了很多工作将它添加到语言中,所以使用它吧!