Недопустимые операции с несколькими потоками из BackgroundWorker2_RunWorkerCompleted в C # - PullRequest
2 голосов
/ 12 мая 2010

Я получаю сообщение об ошибке, которое не имеет смысла.

Cross-thread operation not valid: Control 'buttonOpenFile' accessed from a thread other than the thread it was created on.

В моем приложении поток пользовательского интерфейса отключается backgroundWorker1, который при почти полном завершении запуска backgroundWorker2 и ожидает его завершения.backgroundWorker1 ожидает завершения backgroundWorker2 до его завершения.AutoResetEvent переменные используются для отметки, когда каждый из рабочих завершается.В backgroundWorker2_RunWorkerComplete вызывается функция, которая сбрасывает элементы управления формы.Именно в этой функции ResetFormControls() создается исключение.Я думал, что было безопасно изменять элементы управления формой в функции RunWorkerCompleted.Оба фоновых рабочих создаются из потока пользовательского интерфейса.Вот кратко изложенная версия того, что я делаю:

  AutoResetEvent evtProgrammingComplete_c = new AutoResetEvent(false);
  AutoResetEvent evtResetComplete_c = new AutoResetEvent(false);

  private void ResetFormControls()
  {
     toolStripProgressBar1.Enabled = false;
     toolStripProgressBar1.RightToLeftLayout = false;
     toolStripProgressBar1.Value = 0;

     buttonInit.Enabled = true;
     buttonOpenFile.Enabled = true; // Error occurs here.
     buttonProgram.Enabled = true;
     buttonAbort.Enabled = false;
     buttonReset.Enabled = true;
     checkBoxPeripheryModule.Enabled = true;
     checkBoxVerbose.Enabled = true;
     comboBoxComPort.Enabled = true;
     groupBoxToolSettings.Enabled = true;
     groupBoxNodeSettings.Enabled = true;
  }

  private void buttonProgram_Click(object sender, EventArgs e)
  {
     while (backgroundWorkerProgram.IsBusy)
        backgroundWorkerProgram.CancelAsync();

     backgroundWorkerProgram.RunWorkerAsync();
  }

  private void backgroundWorkerProgram_DoWork(object sender, DoWorkEventArgs e)
  {
     // Does a bunch of stuff...

     if (tProgramStat_c == eProgramStat_t.DONE)
     {
        tProgramStat_c = eProgramStat_t.RESETTING;

        while (backgroundWorkerReset.IsBusy)
           backgroundWorkerReset.CancelAsync();

        backgroundWorkerReset.RunWorkerAsync();
        evtResetComplete_c.WaitOne(LONG_ACK_WAIT * 2);

        if (tResetStat_c == eResetStat_t.COMPLETED)
           tProgramStat_c = eProgramStat_t.DONE;
     }
  }

  private void backgroundWorkerProgram_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
     // Updates form to report complete.  No problems here.

     evtProgrammingComplete_c.Set();
     backgroundWorkerProgram.Dispose();
  }

  private void backgroundWorkerReset_DoWork(object sender, DoWorkEventArgs e)
  {
     // Does a bunch of stuff...

     if (tResetStat_c == eResetStat_t.COMPLETED)
        if (tProgramStat_c == eProgramStat_t.RESETTING)
           evtProgrammingComplete_c.WaitOne();
  }

  private void backgroundWorkerReset_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
     CloseAllComms();
     ResetFormControls();
     evtResetComplete_c.Set();
     backgroundWorkerReset.Dispose();
  }

Будем благодарны за любые ваши мысли или предложения.Я использую Microsoft Visual C # 2008 Express Edition.Спасибо.

Ответы [ 3 ]

5 голосов
/ 12 мая 2010

RunWorkerCompleted будет выполняться в потоке, который запустил BackgroundWorker.Поскольку вы объединяете BackgroundWorkers (начиная с 2 из 1), RunWorkerCompleted 2 выполняется в потоке 1, а не в потоке пользовательского интерфейса.

Вы захотите выполнить маршалинг обратно в поток пользовательского интерфейса с помощью Invoke или переместить обновление пользовательского интерфейсаRunWorkerCompleted до 1.

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

1 голос
/ 12 мая 2010

Объекты BackgroundWorker не могут быть вложенными. Я рекомендую использовать задачи .NET 4.0, если это вообще возможно, так как они работают.

Вложение BGW возможно только при использовании чего-то вроде ActionDispatcher из библиотеки Nito.Async .

0 голосов
/ 12 мая 2010

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

Ваш вопрос: «Как я могу получить доступ к элементу управления, созданному потоком пользовательского интерфейса, с помощью другого потока?»

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

Делает ли это то, что вам нужно?

...