为什么foreach循环中的OpenReadAsync会挂起UI线程?

时间:2016-03-12 16:05:51

标签: c# win-universal-app

我正在为UWP开发一款应用。

我需要加载一个包含大约700张小图片的文件夹。这是我用来将图片加载到内存中的方法:

    private async Task<ObservableCollection<ImageSource>> LoadPicturesAsync()
    {
        var pictureList = new ObservableCollection<ImageSource> { };
        pictureFiles.ForEach(async file =>
        {
            var img = new BitmapImage();
            pictureList.Add(img);
            var stream = await file.OpenReadAsync();
            await img.SetSourceAsync(stream);
        });

        return pictureList;
    }

当我的视图模型的构造函数调用此方法时,视图似乎被阻止(无响应)大约6秒。

这很奇怪,因为所有IO操作都是异步完成的,并且UI线程中唯一运行的是在foreach循环中创建BitmapImage对象。这是如此之快,它不应该阻止UI线程。

我的问题是:为什么UI线程阻塞了6秒,因为我知道我是异步运行所有IO操作的?以及如何修复这个UI线程没有被阻止?

这就是我称之为该方法的方式:

    private async Task Init()
    {
        PictureList = await LoadPicturesAsync();
    }

    //constructor
    public MainVewModel(){
        Init();
    }

1 个答案:

答案 0 :(得分:0)

  

为什么会这样?

因为您正在尝试同时运行这么多线程,因为您正在使用列表的ForEach方法并将异步操作传递给它。用for-each替换ForEach,你就没事了。

private async Task<ObservableCollection<ImageSource>> LoadPicturesAsync() {        
    foreach (var file in pictureFiles) {
       var stream = await file.OpenReadAsync();
       var image = new BitmapImage(); 
       await image.SetSourceAsync(stream);
       pictureList.Add(image );
    }
}       

private async Task Init() {
    PictureList = await LoadPicturesAsync();
}

毕竟,我看到你在视图模型中调用了Init方法:

//constructor
public MainVewModel(){
    Init();
}

由于Init返回一个Task,你需要知道在构造完成时可能没有设置PictureList属性/字段,所以如果你在实例化后立即尝试访问它,你可能会遇到NullReferenceException。

MainViewModel viewModel = new MainViewModel();
var pics = viewModel.PictureList;
var count = pics.Count; // High chance of NullReferenceException

为了避免这种情况,您可以考虑为视图模型定义静态CreateAsync方法。可以找到更多信息here