2016-03-12 3 views
1

Я разрабатываю приложение для UWP.Почему OpenReadAsync внутри цикла foreach висит в потоке пользовательского интерфейса?

Мне нужно загрузить папку, содержащую около 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 секунд.

Это странно, потому что все операции ввода-вывода выполняются асинхронно, и единственное, что работает в потоке пользовательского интерфейса, - это создание объектов BitmapImage в цикле foreach. Это так быстро, что он не должен блокировать поток пользовательского интерфейса.

Мой вопрос: почему блок потока пользовательского интерфейса в течение 6 секунд знал, что я выполняю все операции ввода-вывода асинхронно? И как исправить это, так что поток пользовательского интерфейса не заблокирован?

Это, как я называю этот метод:

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

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

Как и где вы называете Init()? Где называется ваша конструкция модели? Вы пытались создать только новую коллекцию в конструкторе: 'PictureList = new ObservableCollection ();' и затем заполнить сбор асинхронно вне конструктора? ('... await img.SetSourceAsync (поток); PictureList.Add (img);' - измененный порядок) – Romasz

+0

Какой тип 'pictureFiles' и откуда он? –

+0

@Romasz см. Мой обновленный вопрос. – disklosr

ответ

0

Почему это происходит?

Потому что вы пытаетесь запустить так много потоков одновременно, так как вы используете метод ForEach в списке и передаете в него асинхронное действие. Замените 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 возвращает задачу, вам нужно знать, что PictureList свойства/поле не может быть установлены когда строительство завершается, поэтому, если вы попытаетесь получить к нему доступ сразу после создания экземпляра, вы можете столкнуться с исключением NullReferenceException.

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

Чтобы избежать этого, вы можете рассмотреть возможность определения статического метода CreateAsync для модели view. Более подробную информацию можно найти here.

+0

Мой метод init работает в потоке ui, но он мгновенно возвращается и работает (чтение файлов в потоки) является асинхронным, поэтому он не должен блокировать интерфейс пользователя? Создание 700 объектов BitmapSource выполняется быстро и не блокирует пользовательский интерфейс. Это загрузка файлов, которые представляют проблему. – disklosr

+0

Остальная работа работает на ui, но достаточно быстро, чтобы не блокировать ее на 6 секунд. Это просто создает пустые объекты и добавляет их в список. Чтение из файла и загрузка изображений НЕ запускается в потоке пользовательского интерфейса. – disklosr

+0

Кроме того, что касается вашего ответа, создание объектов BitmapImage уже выполняется в потоке пользовательского интерфейса в моей версии кода. Разделение между созданием объектов в пользовательском интерфейсе и их загрузкой в ​​фоновом режиме - это то, что мой код уже делает. – disklosr

Смежные вопросы