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

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

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();
        }
    }
}

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

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

Существует также еще один странный ошибочный случай. В коде ниже:

        public void GetBitmap(int imageID, Action<BitmapImage> callback)
        {
            // Get from server 
            bitmapCallback = callback;

            memorialFileServiceClient.GetImageCompleted += new EventHandler<GetImageCompletedEventArgs>(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 в фоновом потоке. Почему я не могу создать новый объект BitmapImage на фоновом потоке?

26
задан Ender 17 December 2009 в 22:10
поделиться

2 ответа

In order to update a DependencyProperty in a ViewModel, use the same dispatcher you would use to access any other UIElement:

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

Also, BitmapImages have to be instantiated on the UI thread. This is because it uses DependencyProperties, which can only be used on the UI thread. I have tried instantiating BitmapImages on separate threads and it just doesn't work. You could try to use some other means to store images in memory. For example, when you download the image, store it in a MemoryStream. Then a BitmapImage on the UI thread can set its source to the MemoryStream.

You may try instantiating the BitmapImages on the UI thread and then do everything else with the BitmapImage on another thread... but that would get hairy and I'm not even sure it would work. The following would be an example:

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);
62
ответ дан 28 November 2019 в 06:36
поделиться

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

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

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

MSDN при потоково-безопасных вызовах UI

2
ответ дан 28 November 2019 в 06:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: