在调用 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
.
您可以:
- 阻塞调用线程,方法是
Task.Delay(5000).Wait()
(与 Thread.Sleep(5000)
的作用相同)
- 异步等待从
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"。我们做了很多工作将它添加到语言中,所以使用它吧!
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 callingTask.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
.
您可以:
- 阻塞调用线程,方法是
Task.Delay(5000).Wait()
(与Thread.Sleep(5000)
的作用相同) - 异步等待从
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"。我们做了很多工作将它添加到语言中,所以使用它吧!