Вопросы о BackgroundWorker

У меня есть UI приложение WPF.

У May кем-то есть какие-либо идеи, почему этот код не работает?

Случай 1:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate
{
   //some logic
};

worker.RunWorkerAsync();

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

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate 
{ 
  this.Dispatcher.BeginInvoke(
    new Action(() =>  { //my code here }), null); 
}; 

После, что UI, замораживаемый во время выполнения этого кода. Как он выполняется в том же потоке

Случай 2:

BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerAsync(new Action(() =>
{
 //some code here
}));

В этом случае кодируйте в Действии, не выполняемом.

//----------------------------------ОБНОВЛЕННЫЙ--------------------------------------//
Спасибо парень для справки причина, почему мой код не работал правильно, упоминается ниже. Я действительно получал доступ к некоторым элементам UI в фоновом потоке. Теперь я получаю все значения от элементов UI перед вызовом к BackgroundWorker. Я теперь объявляю новые переменные, присваиваю им всем значения от необходимых элементов UI, и затем я передаю эти переменные BackgroundWorker вместо элементов UI (который является тем, что он первоначально сделал).

1
задан noelicus 10 December 2012 в 10:06
поделиться

3 ответа

Проблема с первой версией заключается в том, что внутри «некоторой логики» вы, вероятно, обращаетесь к объектам WPF - вы не можете этого сделать, к объектам WPF можно получить доступ только из того же потока, который их создал.

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

Третья версия, как сказал Джон Скит, проста в неправильном использовании и не должна работать так, как вы думаете.

Итак, что вам нужно сделать?

Вам нужно собрать всю информацию из пользовательского интерфейса в основном потоке перед запуском фонового рабочего, вы можете использовать только простые типы (string, int, double и т. Д.) ) и поточно-ориентированные классы / структуры, вы не можете использовать какие-либо классы WPF в коде, выполняемом BackgroundWorker.

После того, как вы закончили сбор всех данных, вы можете вызвать RunWorkerAsync, в вашем обработчике DoWork вы не можете читать данные из пользовательского интерфейса - вы можете получить доступ только к данным, которые вы подготовили ранее, также вы не можете писать в пользовательский интерфейс - вы должны сохраните его в другом месте (например, член класса) и скопируйте в пользовательский интерфейс после завершения BackgroundWorker.

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

1
ответ дан 2 September 2019 в 22:47
поделиться

Вторая версия не будет делать то, что вы хотите, потому что параметр перегрузки RunWorkerAsync , который принимает параметр, должен быть просто значением для DoWorkEventArgs.Argument . Это не предназначено для выполнения действия - если вы не предоставили обработчик событий, который преобразует значение в действие Action и вызывает его ...

Первая версия должна работать - но согласно комментарию Одеда , вы не указали, что вы имеете в виду под словом "не работает" ...вы также не указали, являются ли обе версии ошибочными или только одна.

1
ответ дан 2 September 2019 в 22:47
поделиться

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

Простой пример:

MyClass foo = new MyClass();
worker.DoWork += delegate
{
   foo.MyProperty++;
};

Я обычно довольно экономно использую анонимные методы. Замыкания создают всевозможные проблемы, если вы не используете их намеренно. И если вы используете их намеренно, легко написать код, который будет слишком тонким, чтобы его можно было изменить через год. Написание именованного метода с явными параметрами может занять немного больше времени и кода, но это не сложнее, чем документировать то, что вы делаете с помощью замыкания.

Я вообще не использую анонимные методы с BackgroundWorker . Существует не только риск того, что вы будете полагаться на закрытие, не задумываясь обо всех его последствиях, но также, очень вероятно, что вы написали код, который не может быть модульно протестирован.

0
ответ дан 2 September 2019 в 22:47
поделиться
Другие вопросы по тегам:

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