C# 在调用Wait之前访问任务的结果实际上是做什么的?
C# 在调用Wait之前访问任务的结果实际上是做什么的?,c#,task-parallel-library,C#,Task Parallel Library,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;
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”。我在我的问题中解决了这个问题,但将其保留在这里作为评论和答案参考
访问Task
上的任何此类属性是否可以作为调用Task.Wait()
的快捷方式
对
发件人:
访问[Result
]属性的get访问器会阻止调用线程,直到异步操作完成;这相当于调用该方法
然而,你的测试并没有达到你认为的效果
Task.Delay(..)
返回一个任务
,该任务在指定的时间后完成。它不会阻止调用线程
因此,()=>{Task.Delay(5000);return 123;}
只需创建一个新的任务
(将在5秒内完成),然后将其丢弃并立即返回123
您可以:
通过执行Task.Delay(5000).Wait()
(其作用与thread.Sleep(5000)
)相同)来阻止调用线程
异步等待从Task.Delay
返回的Task
完成:Task.Run(异步()=>{wait Task.Delay(5000);返回123;})
测试不会等待Task.Delay()
,因此它会立即返回。应该是:
var r = Task.Run(async () => { await Task.Delay(5000); return 123; }).Result;
Result
的行为定义良好-如果任务尚未完成,它将阻塞,直到完成为止。访问其他任务属性不会阻止您问了两个问题。首先,访问结果
是否会隐式导致同步等待
?对但更重要的问题是:
这样做安全吗
这样做不安全。
很容易陷入这样一种情况,即您正在同步等待的任务在它完成到您刚刚进入睡眠状态的线程之前,已经安排了将来要运行的工作。现在我们遇到了这样一种情况,在休眠线程完成某些工作之前,线程不会醒来,因为它处于休眠状态,所以它从来不会这样做
如果您已经知道任务已经完成,那么同步等待结果是安全的。如果没有,则同步等待是不安全的
现在,你可能会说,假设我通过其他方法知道同步等待一个不完整的任务是安全的。那么等待安全吗?好吧,根据这个问题的假设,是的,但是等待可能还是不明智的。记住,异步的全部要点是在高延迟的世界中高效地管理资源。如果您正在同步等待异步任务完成,那么您将强制一个工作进程睡眠,直到另一个工作进程完成;睡觉的工人可能在工作!异步的全部目的是避免工作人员空闲的情况,所以不要强迫他们这样做
wait
是一种异步等待。这是一种等待,意思是“等待运行当前工作流的其余部分,直到完成此任务,但在等待时找到要做的事情”。我们做了很多工作来将它添加到语言中,所以请使用它 它会阻塞,就好像调用了.Wait()
。测试从不等待Task.Delay()
。不,访问任何属性都不是Wait()
的快捷方式。Result
和Wait()
的行为定义良好-Wait()
在我们不需要任何结果时会阻塞<代码>结果
阻塞并返回结果答案中尚未给出的要点是:除非您知道任务已完成,否则切勿使用等待或结果。很容易进入这样一种情况,即任务已将计划的工作安排到您刚刚休眠的线程上,而现在任务将永远不会完成,线程将永远不会醒来,因为将处理任务的工作人员正在休眠,直到任务完成!异步被设计成异步的;强制同步是危险的。@EricLippert只等待已经发生的事情?这似乎很不直观!这个评论真的应该是一个答案。将添加一个。是的,我只是想做一个任务
,模拟一些需要几秒钟的事情。@Mr.Boy:永远不要调用线程。睡眠
使用非零参数,除了模拟工作的测试用例,就像你在这里做的那样。当然,千万不要在生产代码中使用它来设置延迟。如果你在一个有真正员工的办公室里有一个工作流程,你永远不会说“这封邮件应该在下个月一号发出。玛丽,睡觉吧,为下个月设置闹钟,然后发送邮件;我们会在你睡觉的时候继续支付你的工资和福利”。工人是昂贵的资源,异步的全部目的就是不让他们睡觉@EricLippert:我见过一些人(例如)为此推荐Thread.Sleep(1)
overThread.Sleep(0)
。不同之处在于Thread.Sleep(1)
无条件地从调度程序中删除线程,允许其他线程使用时间片,即使这些线程的优先级较低。@Brian:是的,“始终听从Joe的建议”是我多线程编程的基本规则之一,紧跟在“如果可以避免,就不要这样做”之后:)别客气。一个整洁的特性,可以作为一行程序运行和等待任务。感谢您的回答。这主要是为了建立一个面向任务的代码库,并使用简单的案例进行测试。我在想这会不会有什么不同?我还在想