如何异步初始化静态类

Ale*_*kiy 3 c# wpf wcf asynchronous async-await

我有一个Singleton(好吧,它可以是一个静态类,无所谓),这是我的WPF应用程序的一些数据的外观.我想通过WCF加载此数据异步.这是我的实现:

public class Storage
{
    private static readonly Lazy<Storage> _lazyInstance = new Lazy<Storage>(()=>new Storage());

    public static Storage Instance
    {
        get { return _lazyInstance.Value; }
    }

    private Storage()
    {
        Data = new Datastorage(SettingsHelper.LocalDbConnectionString);
        InitialLoad().Wait();
    }

    public Datastorage Data { get; private set; }

    private async Task InitialLoad()
    {
        var tasks = new List<Task>
        {
            InfoServiceWrapper.GetSomeData()
            .ContinueWith(task => Data.StoreItem(task.Result)),
            InfoServiceWrapper.GetAnotherData()
            .ContinueWith(task => Data.StoreItem(task.Result)),
            InfoServiceWrapper.GetSomeMoreData()
            .ContinueWith(task => Data.StoreItem(task.Result)),
        };
        await Task.WhenAll(tasks.ToArray());
    }
}
Run Code Online (Sandbox Code Playgroud)

我从我的ViewModel访问这个类,如下所示:

public class MainWindowViewModel:ViewModelBase
{
    public  SensorDTO RootSensor { get; set; }
    public  MainWindowViewModel()
    {
        var data = Storage.Instance.Data.GetItem<SensorDTO>(t=>t.Parent==t);
        RootSensor = data;
    }
}
Run Code Online (Sandbox Code Playgroud)

在我看来,我有RootSensor的绑定.一切都很好,但我有一个问题:所有我的异步代码执行,然后我发现死锁InitialLoad().Wait();.我知道它涉及WPF UI线程,但不明白如何解决这个问题.

我会感激任何帮助!

Tod*_*ier 8

您基本上遇到了异步/等待的限制:构造函数不能标记为异步.解决问题的正确方法不是通过Wait构造函数调用.这是作弊 - 它会阻止,使你所有的好的异步都变得没有意义,更糟糕的是它会在你发现时引发死锁.

正确的方法是重构您的Storage类,以确保所有异步工作都是从异步方法而不是构造函数完成的.我建议通过用方法替换你的Instance属性来做到这一点GetInstanceAsync().由于这是获取单例实例的唯一公共接口,因此您将确保始终调用InitialLoad(我将重命名InitialLoadAsync).

public class Storage
{
    private static Storage _instance;

    public static async Task<Storage> GetInstanceAsync()
    {
        if (_instance == null)
        {
            // warning: see comments about possible thread conflict here
            _instance = new Storage();
            await _instance.InitialLoadAsync();
        }
        return _instance;
    }

    private Storage() 
    {
        Data = new Datastorage(SettingsHelper.LocalDbConnectionString);
    }

    // etc

 }
Run Code Online (Sandbox Code Playgroud)

现在,你如何在没有阻塞的情况下Storage.GetInstanceAsync()MainWindowViewModel构造函数调用?你可能已经猜到了,你做不到,所以你需要同样地重构它.就像是:

public class MainWindowViewModel : ViewModelBase
{
    public  SensorDTO RootSensor { get; set; }

    public async Task InitializeAsync()
    {
        var storage = await Storage.GetInstanceAsync()
        RootSensor.Data.GetItem<SensorDTO>(t=>t.Parent==t);
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,无论什么电话都await MainWindowViewModel.InitializeAsync()需要被标记async.async/await据说通过你的代码像僵尸病毒一样传播,这很自然.如果在调用堆栈中的任何地方你用一个.Wait()or 打破了那个循环.Result,你就会遇到麻烦.