首页 > 解决方案 > 在 C# 中执行异步操作的正确方法

问题描述

我有一个类需要很长时间才能启动,因为它在构造函数中执行了一些长时间运行的操作。我需要创建 3 个这样的对象并将它们添加到字典中。

我正在尝试使用 async 来创建 3 个线程,每个线程都启动这些对象中的一个,但发现它有点令人困惑。

最初我以为我会等到所有线程都完成,然后将结果添加到字典中,但是我无法编译代码,所以我求助于做字典。在线程中添加(这是好的?)

我写了以下代码

public void CreateAccountStuffs(ICollection<Account> accounts)
{
    CreateStuff(accounts);
    
}

        
private async void CreateStuff(ICollection<Account> accounts)
{
     var tasks = accounts.Select(a => CreateStuffForAccount(a));
     await Task.WhenAll(tasks);
}

private Task CreateStuffForAccount(Account account)
{
        //Long Running process due to website calls in AccountWrapper construction
        var accountWrapper = new AccountWrapper(account);
                
        
        return Task.FromResult(accountWrapper);
}

最后一行似乎毫无意义,但没有它我无法编译代码。我觉得我在这里遗漏了一些非常明显的东西,但是我的多线程技能太生疏了,我很难看到什么。

我的直觉是,有一种更简单的方法来编写我需要做的事情,但我不知道那是什么。

例如,我注意到这个问题似乎表明您可以编写一个返回 Task 的方法,并简单地在方法主体中编写返回“This String”,但是,该构造不会为我编译。我收到错误无法从字符串转换为任务

注意 - 编辑删除不属于问题的代码 - 之前涉及 2 个构造函数,这使事情变得混乱 - 这是 AccountWrapper 中的构造函数长期运行。

标签: c#asynchronoustask

解决方案


将异步方法放在构造函数中而不等待它不是一个好的设计。因为当你的代码从构造函数返回时,如果构造已经完成并且你的类实例可能处于不一致的状态,你现在不会这样做。您可以简单地创建静态工厂方法

public class MyClass
{
    private readonly IDictionary<int, IAccountStuff> _accountStuffs;
    private readonly ICollection<Account> _accounts;

    protected MyClass()
    {
        _accounts = dataLayer.GetAccounts();
        // remove it
        // CreateStuff(accounts);
    }

    private async void CreateStuff(ICollection<Account> accounts)
    {
        var tasks = accounts.Select(a => CreateStuffForAccount(a));
        await Task.WhenAll(tasks);
    }

    private Task CreateStuffForAccount(Account account)
    {
        //Long Running process due to website calls in AccountWrapper construction
        return Task.Run(async () =>
        {
            // do something with you account
            // to do this you dictionary should be concurrent
            this._accountStuffs.Add(account.Id, accountWrapper.Stuff);
        });
    }

    // factory method
    public static async Task Create()
    {
        var myClass = new MyClass();
        await myClass.CreateStuff(); 
    }
}


....
// and somwhere you may use
public async Task Foo()
{
    var myClass = await MyClass.Create();
    // now you can safely use you class instance myClass 
}

推荐阅读