2012-01-04 2 views
1

Я меняю Text кнопки от BackgroundWorker, и он работает. Я думал, что это должно было сделать исключение. Почему не так?Почему CAN BackgroundWorker изменяет компоненты пользовательского интерфейса?

Почему я не могу получить Cross-thread operation not valid: ... accessed from a thread other than the thread it was created on.?

EDIT: Спасибо всем.

Возможно, причина в том, что в пользовательском интерфейсе был: Thread.Sleep(1000);.

public Form1() 
{ 
    InitializeComponent(); 

    backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); 
    backgroundWorker1.RunWorkerAsync(); 
    Thread.Sleep(1000); 
} 

void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    button1.Text = "a"; 
} 

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

public partial class Form1 : Form 
{ 
    int i; 

    public Form1() 
    { 
     InitializeComponent(); 

     i = 1; 
     backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); 
     backgroundWorker1.RunWorkerAsync(); 
     for (int j = 0; j < 100000000; j++) ; 
     button1.Text = i.ToString(); 
    } 

    void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
     i = 2; 
    } 
} 

Почему?

+0

показать свой исходный код ... – Yahia

+0

Что это? 'for (int j = 0; j <100000000; j ++); button1.Text = i.ToString(); 'Вы имеете в виду' for (int j = 0; j <100000000; j ++) {button1.Text = i.ToString(); } '? – user973511

+0

@Fuex No. Цикл должен доказать, что 'i' изменяется с помощью BGW _, пока поток пользовательского интерфейса работает_. Его «Текст» заканчивается как «2». – ispiro

ответ

0

Ответ, вероятно, что операции поперечной резьбы можно выполнить. Они просто склонны к неприятностям. И Control (который должен выбросить исключение), вероятно, не «перепутал» резьбу, когда это Sleep.

5

Функция обратного вызова, вызванная фоновым работником после завершения работы (BackgroundWorker.RunWorkerCompleted) - для вашего удобства - с помощью диспетчера потоков пользовательского интерфейса.

Edit:

@ispiro: Это не гарантирует, что код в вашем втором примере будет всегда работа - у вас еще есть обновление кросс-нить переменной i так что вы должны объявить его volatile к убедитесь, что он всегда обновляется правильно.

Причина, по которой первый код не должен, заключается в том, что .NET framework помогает вам обнаружить этот сквозной доступ. Как отметил @Greg D, это может быть отключено (хотя это определенно не-нет). Для получения дополнительной информации посетите этот MSDN page:

В .NET Framework позволяет определить, когда вы обращаетесь к своему управления таким образом, чтобы не Потокобезопасные. Когда вы запускаете ваше приложение в отладчике, а поток, отличный от одного , который создал элемент управления, пытается вызвать этот элемент управления, отладчик вызывает исключение InvalidOperationException с сообщением «Control control », доступ к которому осуществляется из потока, отличного от поток, на котором он был создан. "

Это исключение достоверно возникает во время отладки и, в некоторых случаях, во время выполнения. Вы можете увидеть это исключение, если вы отлаживаете приложения, которые вы написали с .NET Framework до версии .NET Framework версии 2.0. Настоятельно рекомендуется исправить эту проблему , когда вы ее видите, но вы можете отключить ее, установив для свойства CheckForIllegalCrossThreadCalls значение false. Это приводит к тому, что ваш контроль запускается, как если бы он выполнялся под Visual Studio .NET 2003 и .NET Framework 1.1.

+0

Спасибо. (Я обновил вопрос.) – ispiro

+0

«Причина, по которой первый код не должен работать, - это ...» - это, по сути, мой вопрос. Потому что это _does_.И я полагаю, что ответ заключается в том, что «Сон» помогает разрешать конфликты потоков. Я все еще не понимаю, почему, второй работает. (VS не бросает никаких исключений ни в одном.) Большое спасибо за 'volatile'! – ispiro

1

Поскольку функция обратного вызова для события RunWorkerCompleted вызывается (вызывается) в потоке пользовательского интерфейса для удобства. Поймите, однако, что это неверно для каждый случай, очевидно, что обратный вызов DoWork работает по отдельному потоку.

+0

Спасибо. (Я обновил вопрос.) – ispiro

1

Есть несколько возможностей:

1) Ваша программа отключил проверку кросс-нить. Это печально распространенный хак, который использует программное обеспечение, когда люди не понимают правила потоковой передачи вокруг потока пользовательского интерфейса.

2) Ваша программа изменяет пользовательский интерфейс с помощью событий ProgressWorker или Completed. Эти события сопоставляются с контекстом синхронизации потока, создавшего BackgroundWorker. Если BackgroundWorker используется в классическом контексте как компонент разработчика WinForms, вы являетесь золотым. События собираются на поток пользовательского интерфейса для вас, поэтому вам не нужно делать такую ​​глупость самостоятельно.

+0

Спасибо. (Я обновил вопрос.) – ispiro

1

Этот вопрос вводит в заблуждение, так как BackgroundWorder предоставляет только определенные гарантии. Ниже приводится «Примечание» из документации Б:

Вы должны быть осторожны, чтобы не [не] манипулировать какие-либо объекты пользовательского интерфейса в вашем DoWork обработчика событий. Вместо этого вы можете связаться с пользовательским интерфейсом через события ProgressChanged и RunWorkerCompleted.

В RunWorkerCompleted и ProgressChangedсобытия размещены в потоке, который создал BGW *. Же гарантия не имеет для DoWork события, однако: не доступа к пользовательскому интерфейсу внутри него :)

В сущности, код в RunWorkerCompleted и ProgressChanged эффективно автоматически завернутой в Control.BeginInvoke, который «сообщения сообщение для вызова обратного вызова "в очередь отправки окна/потока.

Счастливое кодирование.


* Поскольку поток, который создал (или, возможно, это инициировал?) Эффекты BGW, где будут размещены обратные вызовы, можно создать BGW, что не будет «бежать» на желаемое Пользовательский интерфейс. Чтобы избежать этого нечетного поведения, всегда создает/запускает BGW в потоке пользовательского интерфейса, на который он должен отправлять сообщения.

+2

На практике это, как правило, тот же поток. Если мы получим технический C++ - технический, это контекст синхронизации потока. Контекст синхронизации теоретически может размещать его в любом месте, просто практические приложения обычно отправляют его в вызывающий поток. :) –

+0

@GregD Это был личный опыт общения :) Я случайно создал BGW в другом потоке раньше. Веселые времена. –

+0

Спасибо. (Я обновил вопрос.) – ispiro

Смежные вопросы