在构造函数中执行任务 [重复]

Posted

技术标签:

【中文标题】在构造函数中执行任务 [重复]【英文标题】:Executing a Task inside of a constructor [duplicate] 【发布时间】:2020-07-15 17:06:44 【问题描述】:

我一直在尝试为您的类型具有依赖关系并且您想要调用返回Task 的方法的情况找到一种设计。直觉反应是做GetAwaiter().GetResult(),但我知道这违背了异步任务的全部目的。想法是启动任务,但让它做它的事情,直到类型需要它。

public class SomeClass 
    private readonly Task<Data> _getDataTask;
    private readonly IDependency _dep;

    private Data _data;

    public SomeClass(IDependency dep) 
        _dep = dep;
        // I'll spin this up but I don't need the result yet
        _getDataTask = _dep.GetDataAsync();
    

    public async Task DoSomeWork() 
        // Now I need the result of the task so I'll await the task
        _data = await _getDataTask;
        ExecuteWorkOn(_data);
    

如果您没有缓存结果,也许这种方法会产生很多条件语句等待?我希望获得有关这种方法的反馈,希望可以链接另一个 SO 问题,或者我们提出以前没有考虑过的设计。

更新 1 我将Task 设为Task&lt;Data&gt;,如其中一个cmets 中所述

【问题讨论】:

这种方法没有错 如果没有人打电话给DoSomeWork,那么GetDataAsync的电话是不是就浪费了?如果是这样,您似乎可以在这里使用Lazy 来推迟拨打电话,直到需要。 不应该_getDataTaskTask&lt;T&gt; @Damien_The_Unbeliever 我在 Stephen Cleary 的博客上读到了关于自定义构建的 AsyncLazy 类型,它允许您定义如何获取 T 并在需要时等待它。我也考虑过,如果没有人调用等待结果的方法,结果就被浪费了。 有一个few different approaches,这取决于您的需要。或者,如果这是一个 ViewModel 并且异步工作正在加载数据,那么you'd need a solution like this。 【参考方案1】:

这个问题有两个很好的解决方案:

第一:

在构造函数中使用异步初始化方法并将生成的Task 保存在属性中。这样调用代码可以等待初始化完成。

public class Foo

    public Task InitTask  get; private set; 

    public Foo()
    
        this.InitTask = this.Init();
    

    private async Task Init()  ... 

可以这样使用


var newFoo = new Foo();
await newFoo.InitTask();
// can now use newFoo

第二:

使用仅使用的私有构造函数并拥有一个 Create 方法,您可以使用它为您的类创建实例

public class Foo

    public Foo()  

    public async Task<Foo> Create() 
    
        var newFoo = new Foo();
        await newFoo.Init();
        return newFoo;
    

    private async Task Init()  ... 

可以这样使用

var newFoo = await Foo.Create();

您的方法(如果不错的话)是方法 1 的变体,但这意味着您需要在每个需要任务结果(或副作用)的方法中等待构造函数中启动的任务。

public class Foo

    private Task InitTask  get; private set; 

    public Foo()
    
        this.InitTask = this.Init();
    

    private async Task Init()  ... 

    public async Task DoStuffA() 
    
        await this.InitTask;
        // do work
    

    public async Task DoStuffB() 
    
        await this.InitTask;
        // do work
    

    public async Task DoStuffC() 
    
        await this.InitTask;
        // do work that could be done without async/await
    

所以我建议使用方法 2 进行异步初始化。

【讨论】:

在我的工作中,我确实经常使用构建器模式来构建不可变类型。也许构建器的异步版本会按照您await builder.BuildAsync() 的顺序排列。我想知道 AsyncLazy 是否会更好,您可以设置如何获取数据,但在您执行 await 之前不要获取它。 Re: " 我想知道 AsyncLazy 是否会更好" 我会说这取决于实例的构建和使用之间经过了多少时间是否保证使用数据。

以上是关于在构造函数中执行任务 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中编写复制构造函数和赋值运算符的清单

Kotlin 协程协程取消 ③ ( finally 释放协程资源 | 使用 use 函数执行 Closeable 对象释放资源操作 | 构造无法取消的协程任务 | 构造超时取消的协程任务 )

Kotlin 协程协程取消 ③ ( finally 释放协程资源 | 使用 use 函数执行 Closeable 对象释放资源操作 | 构造无法取消的协程任务 | 构造超时取消的协程任务 )

Python执行任务[重复]

github上的每日学习 10

如何在构造函数中使用边界[重复]