强制C#异步任务是懒惰的吗?

Seb*_*edl 18 .net c# asynchronous .net-core

我有一种情况,我有一个特殊工厂创建的对象树.这有点类似于DI容器,但并不完全相同.

对象的创建总是通过构造函数发生,并且对象是不可变的.

在给定的执行中可能不需要对象树的某些部分,应该懒惰地创建.因此构造函数参数应该只是按需创建的工厂.这看起来像是一份工作Lazy.

但是,对象创建可能需要访问慢资源,因此始终是异步的.(对象工厂的创建函数返回一个Task.)这意味着该创建函数Lazy需要是异步的,因此注入的类型需要Lazy<Task<Foo>>.

但我宁愿没有双层包装.我想知道是否有可能强迫一个Task懒惰,即创建一个Task保证在等待之前不执行的东西.据我所知,a Task.Run或者Task.Factory.StartNew可以随时开始执行(例如,如果来自池的线程是空闲的),即使没有什么可以等待它.

public class SomePart
{
  // Factory should create OtherPart immediately, but SlowPart
  // creation should not run until and unless someone actually
  // awaits the task.
  public SomePart(OtherPart eagerPart, Task<SlowPart> lazyPart)
  {
    EagerPart = eagerPart;
    LazyPart = lazyPart;
  }

  public OtherPart EagerPart {get;}
  public Task<SlowPart> LazyPart {get;}
}
Run Code Online (Sandbox Code Playgroud)

Max*_*Max 21

我不确定你为什么要避免使用Lazy<Task<>>,它,但如果它只是为了保持API更容易使用,因为这是一个属性,你可以用一个支持字段来做:

public class SomePart
{
    private readonly Lazy<Task<SlowPart>> _lazyPart;

    public SomePart(OtherPart eagerPart, Func<Task<SlowPart>> lazyPartFactory)
    {
        _lazyPart = new Lazy<Task<SlowPart>>(lazyPartFactory);
        EagerPart = eagerPart;
    }

    OtherPart EagerPart { get; }
    Task<SlowPart> LazyPart => _lazyPart.Value;
}
Run Code Online (Sandbox Code Playgroud)

这样,用法就好像它只是一个任务,但初始化是懒惰的,只有在需要时才会产生工作.


pku*_*rov 6

@Max 的回答很好,但我想添加建立在评论中提到的 Stephen Toub 文章之上的版本:

public class SomePart: Lazy<Task<SlowPart>>
{
    public SomePart(OtherPart eagerPart, Func<Task<SlowPart>> lazyPartFactory)
        : base(() => Task.Run(lazyPartFactory))
    {
        EagerPart = eagerPart;
    }

    public OtherPart EagerPart { get; }
    public TaskAwaiter<SlowPart> GetAwaiter() => Value.GetAwaiter();
}
Run Code Online (Sandbox Code Playgroud)
  1. SomePart 显式继承自,Lazy<Task<>>因此很明显它是惰性异步的

  2. 如果该工厂在真正的异步部分之前需要一些 CPU 繁重的工作,则调用基本构造函数包装lazyPartFactoryTask.Run避免长块。如果不是你的情况,只需将其更改为base(lazyPartFactory)

  3. SlowPart 可通过 TaskAwaiter 访问。所以 SomePart 的公共接口是:

    • var eagerValue = somePart.EagerPart;
    • var slowValue = await somePart;

  • @Max 的答案可能不是更好,因为使用组合而不是继承?IsValueCreated 可能会在 API 上下文中被误解,不是吗? (2认同)
  • 是的,完全同意 (2认同)