Недопустимая проблема с доступом к нескольким streamам

У меня есть два classа ViewModel: PersonViewModel и PersonSearchListViewModel. Одним из полей PersonViewModel реализуется образ профиля, который загружается через WCF (кэшируется локально в изолированном хранилище). PersonSearchListViewModel – это контейнерный class, в котором содержится список лиц. Поскольку загрузка изображений относительно тяжелая, PersonSearchListViewModel загружает только изображения для текущей, следующей и предыдущей страницы (результаты отображаются в пользовательском интерфейсе) … для дальнейшего улучшения загрузки изображений я помещаю загрузку изображений в другой stream. Однако многопоточный подход вызывает проблемы с доступом к нескольким streamам.

PersonViewModel:

public void RetrieveProfileImage() { Image profileImage = MemorialDataModel.GetImagePerPerson(Person); if (profileImage != null) { MemorialDataModel.ImageManager imgManager = new MemorialDataModel.ImageManager(); imgManager.GetBitmap(profileImage, LoadProfileBitmap); } } private void LoadProfileBitmap(BitmapImage bi) { ProfileImage = bi; // update IsProfileImageLoaded = true; } private BitmapImage profileImage; public BitmapImage ProfileImage { get { return profileImage; } set { profileImage = value; RaisePropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("ProfileImage")); } } 

PersonSearchListViewModel:

 private void LoadImages() { // load new images Thread loadImagesThread = new Thread(new ThreadStart(LoadImagesProcess)); loadImagesThread.Start(); //LoadImagesProcess(); If executed on the same thread everything works fine } private void LoadImagesProcess() { int skipRecords = (PageIndex * PageSize); int returnRecords; if (skipRecords != 0) { returnRecords = 3 * PageSize; // page before, cur page and next page } else { returnRecords = 2 * PageSize; // cur page and next page } var persons = this.persons.Skip(skipRecords).Take(returnRecords); // load images foreach (PersonViewModel pvm in persons) { if (!pvm.IsProfileImageLoaded) { pvm.RetrieveProfileImage(); } } } 

Как вы обрабатываете данные в classе ViewModel многопоточным способом? Я знаю, что вам нужно использовать диспетчер в пользовательском интерфейсе для обновления. Как вы обновляете ViewModel, привязанный к пользовательскому интерфейсу?

** РЕДАКТИРОВАТЬ **

Также происходит еще одна странная ошибка. В приведенном ниже коде:

  public void GetBitmap(int imageID, Action callback) { // Get from server bitmapCallback = callback; memorialFileServiceClient.GetImageCompleted += new EventHandler(OnGetBitmapHandler); memorialFileServiceClient.GetImageAsync(imageID); } public void OnGetBitmapHandler(object sender, GetImageCompletedEventArgs imageArgs) { if (!imageArgs.Cancelled) { // I get cross-thread error right here System.Windows.Media.Imaging.BitmapImage bi = new BitmapImage(); ConvertToBitmapFromBuffer(bi, imageArgs.Result.Image); // call call back bitmapCallback.Invoke(bi); } } 

Я получаю сквозную ошибку при попытке создать новый объект BitmapImage в фоновом streamе. Почему я не могу создать новый объект BitmapImage в фоновом streamе?

    Чтобы обновить DependencyProperty в ViewModel, используйте тот же диспетчер, который вы использовали бы для доступа к любому другому UIElement:

     System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => {...}); 

    Кроме того, BitmapImages необходимо создать в streamе пользовательского интерфейса. Это связано с тем, что он использует DependencyProperties, который может использоваться только в streamе пользовательского интерфейса. Я попытался создать экземпляр BitmapImages для отдельных streamов, и он просто не работает. Вы можете попытаться использовать некоторые другие средства для хранения изображений в памяти. Например, при загрузке изображения сохраните его в MemoryStream. Затем BitmapImage в streamе пользовательского интерфейса может установить его источник в MemoryStream.

    Вы можете попробовать создать экземпляр BitmapImages в streamе пользовательского интерфейса, а затем сделать все остальное с помощью BitmapImage в другом streamе … но это будет волосатое, и я даже не уверен, что это сработает. Ниже приведен пример:

     System.Windows.Media.Imaging.BitmapImage bi = null; using(AutoResetEvent are = new AutoResetEvent(false)) { System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { bi = new BitmapImage(); are.Set(); }); are.WaitOne(); } ConvertToBitmapFromBuffer(bi, imageArgs.Result.Image); bitmapCallback.Invoke(bi); 

    Я считаю, что у вас проблема с перекрестными streamами с streamом пользовательского интерфейса.

    Редактирование связанного объекта может привести к обновлению пользовательского интерфейса рабочего streamа, что не может быть выполнено. Вероятно, вам придется выполнять InvokeRequired / Invoke hokey-pokey всякий раз, когда вы обновляете связанный class.

    Вы сказали, что знаете это уже, но для справки:

    MSDN по streamобезопасным вызовам в пользовательский интерфейс

    Это может быть достигнуто с помощью WriteableBitmap.

      public void LoadThumbAsync(Stream src, WriteableBitmap bmp, object argument) { ThreadPool.QueueUserWorkItem(callback => { bmp.LoadJpeg(src); src.Dispose(); if (ImageLoaded != null) { Deployment.Current.Dispatcher.BeginInvoke(() => { ImageLoaded(bmp, argument); }); } }); } 

    Но вы должны построить WriteableBitmap в streamе пользовательского интерфейса, тогда загрузка может быть выполнена в другом streamе.

      void DeferImageLoading( Stream imgStream ) { // we have to give size var bmp = new WriteableBitmap(80, 80); imageThread.LoadThumbAsync(imgStream, bmp, this); } 

    См. Больше объяснений в этом сообщении блога

    Давайте будем гением компьютера.