async Task.Run с MVVM - PullRequest
       26

async Task.Run с MVVM

6 голосов
/ 11 декабря 2011

Я играл с новыми асинхронными шаблонами CTP и MVVM.Я конвертировал старую программу, в которой использовался фоновый рабочий, и сообщал о прогрессе для обновления коллекции в моей модели.Я преобразовал это во что-то вроде

TaskEx.Run(async () =>
{
  while (true)
  {
    // update ObservableCollection here
  }
  await TaskEx.Delay(500);
});

На мой взгляд, я связываюсь с моей моделью представления, которая выставляет эту наблюдаемую коллекцию.Однако, когда моя коллекция обновляется, я получаю следующее исключение

. Этот тип CollectionView не поддерживает изменения в его SourceCollection из потока, отличного от потока Dispatcher.

Iя не уверен, что правильный способ вернуть это обратно в поток пользовательского интерфейса, когда это сделано так.

Ответы [ 3 ]

6 голосов
/ 11 декабря 2011

Вам не нужно запускать асинхронные методы, используя Task.Run() или любые другие специальные средства, просто вызовите их. И в вашем случае это именно то, что вызывает проблему.

Данная функция выглядит следующим образом:

Action f = async () =>
{
    while (true)
    {
        // modify the observable collection here
        await Task.Delay(500);
    }
};

Вызывая это так из некоторого метода, запущенного в потоке пользовательского интерфейса, например, обработчик события:

f();

работает точно так, как должно. Он выполняет первую итерацию цикла и затем возвращается. Следующая итерация выполняется через 500 мс (или более, если поток пользовательского интерфейса занят) в потоке пользовательского интерфейса.

С другой стороны, если вы называете это так:

Task.Run(addNames);

это не работает правильно. Причина этого заключается в том, что async методы пытаются продолжить в том же контексте, в котором они были запущены (если вы явно не укажете иное). Первая версия была запущена в потоке пользовательского интерфейса, поэтому она продолжалась в потоке пользовательского интерфейса. Вторая версия началась в потоке ThreadPool (спасибо Task.Run()) и продолжалась там же. Вот почему это вызвало вашу ошибку.

Все это делается с помощью SynchronizationContext, если таковой имеется.

4 голосов
/ 14 декабря 2011

Вы создали ObservableCollection в главном потоке пользовательского интерфейса и пытаетесь обновить его в асинхронном фоновом потоке, чего нельзя сделать в WPF.

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

Обычно мой код для обновления ObservableCollection в фоновом потоке будет выглядеть примерно так:

private async void LoadItems()
{
    Task<List<MyItem>> getItemsTask = Task.Factory.StartNew(GetItems);

    foreach(MyItem item in await getItemsTask)
        MyCollection.Add(item);
}

private List<MyItem> GetItems()
{
    // Make database call to get items
}
3 голосов
/ 02 марта 2012

Хотя верно, что вы не можете обновить ObservableCollection из второго потока, возможно создать асинхронную наблюдаемую коллекцию.Это позволяет вам обновлять коллекцию из задач или из потоков, в которых коллекция не была создана.

Я бы опубликовал пример, но нашел информацию здесь.Это довольно гладко, и я нашел это очень полезным примером.

http://www.thomaslevesque.com/2009/04/17/wpf-binding-to-an-asynchronous-collection/

...