任务 == 懒惰吗?

Posted

技术标签:

【中文标题】任务 == 懒惰吗?【英文标题】:Is Task == Lazy? 【发布时间】:2015-12-18 13:42:34 【问题描述】:
public Data GetCurrent(Credentials credentials)
 
    var data = new Lazy<Data>(() => GetCurrentInternal(credentials));
    try
    
        return data.Value;
    
    catch (InvalidOperationException ex)
    
        throw ex.InnerException;
    

如果我将调用更改为以下内容:

var data = new Task<Data>(() => GetCurrentInternal(credentials));

有什么变化吗?我应该更喜欢Task 而不是LazyDispose()catch(Exception) 呢?

【问题讨论】:

为什么要首先使用Lazy?初始化局部变量是没有意义的。见this。 TaskLazy 是完全不同的类和概念——彼此无关。你想要什么行为? 别偷懒,看文档...msdn.microsoft.com/en-us/library/dd321424(v=vs.110).aspx 当您像在代码中那样重新抛出内部异常时,您会丢失堆栈跟踪,从而难以理解原始异常发生的位置。一般来说,不要重新抛出这样的异常。 1)捕获并处理它,2)捕获它并将其作为新异常重新抛出,并将捕获的异常作为内部异常或 3)如果您真的想重新抛出原始异常(例如,如果您想在异常展开期间记录) 在catch 块中使用throw 而不指定任何异常来重新抛出原始异常。 您正在纳税。 Lazy&lt;T&gt; 是“我今年可能不需要计算我的慈善捐款,所以我将尽可能推迟这项工作,希望我根本不必这样做。但如果我这样做必须,我会记住我计算的结果,这样我就不必再做一次了。” Task&lt;T&gt; 是“亲爱的,当我在做这个其他计算时,你能算出我们的慈善捐款吗?当你完成后告诉我,我可能会也可能不会最终使用你的工作。无论如何,一旦你完成了,记住结果是什么。” 【参考方案1】:

相似之处

Lazy&lt;T&gt;Task&lt;T&gt; 都承诺稍后做一些工作并返回 T 类型的结果。

区别

Lazy&lt;T&gt; 承诺在需要时尽可能晚地完成其工作,并且同步进行。

Task&lt;T&gt; 但是可以在您的线程执行其他工作或阻塞等待结果时异步执行其工作。

当您调用.Value 时,Lazy&lt;T&gt; 会冒泡任何由 lambda 引起的异常。

Task&lt;T&gt; 将保留任何由 lambda 引起的异常,并在您稍后 await task 时抛出它。或者,如果您 task.Wait()可能将异常包装在 AggregationException 中。有关捕获任务引发的异常的更多信息,请参阅此内容:Catch an exception thrown by an async method 和此http://stiller.co.il/blog/2012/12/task-wait-vs-await/

【讨论】:

还有另一个相似之处:在提供的代码中两者都没有意义。 如果我最终在里面使用Task,我需要调用Dispose。我说的对吗? @ivan_petrushenko 可能不请阅读blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx @ivan_petrushenko 只是禁止 CA 警告。它不会阻止你编译代码。 对,这是分析器中的一个错误,确实应该修复。当一次性是一项任务时,它应该抑制警告。【参考方案2】:

不,Lazy&lt;T&gt;Task&lt;T&gt; 不同。


Lazy&lt;T&gt;是lazy evaluation概念的实现:

    它表示一个T 值,该值将在首次访问时同步计算,这可能会发生也可能不会发生。 在第一次访问时进行评估,后续访问使用缓存值。 Memoization 是一个密切相关的概念。 第一次访问将阻塞以计算该值,而后续访问将立即产生该值。

Task&lt;T&gt; 与future 的概念有关。

    它表示一个T 值,该值将在创建promise 时计算(通常是异步),即使最终没有人实际访问它。 所有访问都会产生一个缓存值,该值已经或将由评估机制(承诺)在之前或将来计算。 在值计算完成之前发生的任何访问都将阻塞,直到计算完成。在值计算完成后发生的任何访问都将立即产生计算值。

话虽如此,Lazy&lt;T&gt;Task&lt;T&gt; 确实有一些共同点,您可能已经知道了。

这些类型中的每一个都是一元泛型类型,它描述了执行特定计算的独特方式,这将产生 T 值(并且该计算可以方便地作为委托或 lambda 传递)。换言之,这些类型中的每一个都是monad 的示例。您可以在 Stack Overflow 和其他地方找到一些非常 good and simple explanations of what a monad is 的内容。

【讨论】:

哦,它是一个单子,或者更准确地说,是一个单子。 C# 类型系统创建使用 monad 模式的类型没有问题。 C# 类型系统缺乏的概念是没有办法说“我想做一个方法,其参数只是与 monad 模式匹配的泛型类型”。 @Thomas 很好的答案!【参考方案3】:

TaskLazy 是完全不同的概念。

您使用 Task 执行异步操作。一些无聊的 MSDN:

Task 类表示一个不返回 值,通常异步执行。任务对象是一个 首先介绍基于任务的异步模式的核心组件 在 .NET Framework 4 中引入。因为由 任务对象通常在线程池线程上异步执行 而不是在主应用程序线程上同步,您可以使用 Status 属性,以及 IsCanceled、IsCompleted 和 IsFaulted 属性,用于确定任务的状态。最常见的, 一个 lambda 表达式用于指定任务要完成的工作 执行。

惰性用于对象的延迟初始化。这意味着,只有当您调用 lazyObj.Value 时,您的对象才会被初始化。

使用延迟初始化来推迟创建大型或 资源密集型对象,或资源密集型的执行 任务,特别是当此类创建或执行可能不会发生时 在程序的生命周期内。

要准备延迟初始化,您需要创建一个延迟实例。 您创建的 Lazy 对象的类型参数指定 要延迟初始化的对象的类型。构造函数 用于创建 Lazy 对象的决定 初始化的特点。延迟初始化发生在 第一次访问 Lazy.Value 属性。

【讨论】:

Task 不必是异步的,以TaskCompletionSource&lt;T&gt; 为例。 @YuvalItzchakov 这是真的。任务不一定是异步的。

以上是关于任务 == 懒惰吗?的主要内容,如果未能解决你的问题,请参考以下文章

学习打卡功能上线,告别懒惰,坚持学习!

站立会议14

为了效率而外包,不要因为懒惰外包

Windows 2008 任务调度器任务可以有用户界面吗?

c#load中定义task任务会执行吗

当任务依赖关系过期时,luigi 可以重新运行任务吗?