Вопросы BackgroundWorker - PullRequest
       1

Вопросы BackgroundWorker

1 голос
/ 25 июля 2010

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

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

Дело 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); 
}; 

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

Дело 2:

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

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

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

Ответы [ 3 ]

1 голос
/ 25 июля 2010

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

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

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

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

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

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

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

1 голос
/ 25 июля 2010

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

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

0 голосов
/ 25 июля 2010

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

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

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

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

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

...