Как бороться с исключениями для сквозного потока?

Общим исключением можно получить при работе с несколькими потоками в WPF:

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

Каковы варианты правильного решения этой проблемы?

Ответы

Ответ 1

В зависимости от ситуации существуют различные варианты:

Доступ к элементу управления из другого потока

например. обновление TextBlock с информацией о ходе работы.

  • Связывание данных:

    В этом случае самая простая вещь, которую вы можете сделать, - это избегать прямого взаимодействия с элементом управления. Вы можете просто привязать свойство, к которому хотите получить доступ или изменить объект, класс реализует INotifyPropertyChanged, а затем вместо этого установите свойство на этом объекте. Структура будет обрабатывать остальное для вас. (В общем, вам редко приходится взаимодействовать с элементами UI напрямую, вы можете почти всегда связывать соответствующие свойства и работать с источником привязки, а один случай, когда прямой доступ к управлению может быть необходим, - это авторинг управления.)

    В некоторых случаях, когда привязки данных недостаточно, например, при попытке изменить привязанную ObservableCollection<T>, для этого вам нужно...

  • Отправка:

    Вы можете отправить свой код доступа в поток, владеющий объектом, это можно сделать, вызвав Invoke или BeginInvoke на Dispatcher, которому принадлежит доступ к объекту (получение этого Dispatcher возможно в другом потоке).

    например.

    new Thread(ThisThreadStart).Start();
    
    void ThisThreadStart()
    {
        textBlock.Dispatcher.Invoke(new Action(() => textBlock.Text = "Test"));
    }
    

    Если не понятно, в каком потоке выполняется метод, вы можете использовать Dispatcher.CheckAccess для отправки или выполнения действия напрямую.

    например.

    void Update()
    {
        Action action = () => myTextBlock.Text = "Test";
        var dispatcher = myTextBlock.Dispatcher;
        if (dispatcher.CheckAccess())
            action();
        else
            dispatcher.Invoke(action);
    }
    

    Если объект не является DispatcherObject, и вам все еще нужен связанный Dispatcher, вы можете использовать Dispatcher.CurrentDispatcher в потоке, создающем объект (так что это в методе, выполняемом потоком, не принесет вам пользы). Для удобства, поскольку вы обычно создаете объекты в главном потоке пользовательского интерфейса приложения; вы можете получить этот поток Dispatcher из любого места, используя Application.Current.Dispatcher.

Специальные случаи:

  • BackgroundWorker

    Переместите любой доступ к управлению ProgressChanged, как это происходит в потоке, который создал экземпляр (который, конечно же, должен быть UI-потоком)

  • Таймеры

    В WPF вы можете использовать DispatcherTimer для удобства, он отправляет вам, поэтому любой код в Tick вызывается в соответствующем диспетчере. Если вы можете делегировать отправку в систему привязки данных, вы, конечно же, можете использовать обычный таймер.

Вы можете узнать больше о том, как работает очередь Dispatcher и потоки WPF вообще в MSDN.

Доступ к объекту, созданному в другом потоке

например. Загрузка изображения в фоновом режиме.

Если объект, о котором идет речь, не Freezable, вы должны, как правило, просто не создавать его в другом потоке или ограничивать доступ к создающему потоку. Если это Freezable, вам просто нужно вызвать Freeze, чтобы сделать его доступным для других потоков.

Доступ к объекту данных из другого потока

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

Эта ситуация такая же, как доступ к элементу управления, и могут применяться одни и те же подходы, но обычно их следует избегать в первую очередь. Разумеется, это позволяет получать уведомления об изменении свойств с помощью свойств свойств зависимостей, и эти свойства также могут быть связаны, но достаточно часто это просто не стоит отказываться от ните-независимости. Вы можете получать уведомления об изменениях из INotifyPropertyChanged, а система привязки в WPF по своей природе асимметрична, всегда есть свойство, которое связано (цель) и что-то, что является источником для этой привязки. Обычно пользовательский интерфейс является целью, а данные являются источником, а это означает, что только компоненты пользовательского интерфейса должны нуждаться в свойствах зависимостей.

Ответ 2

Это будет несколько сотен строк кода, для чего-то, что я "понял".

Но резюме:

App_OnStartup генерировать фоновый поток

в обратном вызове,

Вызов

Application.Current.MainWindow.Dispatcher.CheckAccess() - получает исключение Application.Current.Dispatcher.CheckAccess() не

Ответ 3

У меня есть объект-получатель udp, который обменивается событиями, когда метод/обратные вызовы + = 'ed в моем файле wpf.cs mainWindow.

Функции обработчика событий вызываются с параметрами, один из которых является сообщением, которое я хочу отображать в списке в файле mainWindow.cs

Используя информацию в этом потоке H.B. выше; Я добавил, протестировал и обработал crossthread в wpf в моем обратном вызове обработчика события, используя следующий код, но я использую настоящее сообщение не жестко закодированное:

listBox1.Dispatcher.Invoke(new Action(() => listBox1.Items.Add("MessageHere")));

ОБНОВЛЕНИЕ:

Это лучше, потому что вы можете добавить больше вещей в анонимную функцию.

 listBox1.Dispatcher.Invoke((Action)delegate 
 {
     listBox1.Items.Add(e.ReaderMessage); 
 });