2

我收到一个没有意义的错误。

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

在我的应用程序中,UI 线程触发backgroundWorker1,几乎完成时触发backgroundWorker2并等待它完成。 在完成之前 backgroundWorker1等待完成。变量用于标记每个工人何时完成。在调用重置表单控件的函数中。正是在这个函数中引发了异常。我认为在函数中修改表单控件是安全的。两个后台工作人员都是从 UI 线程实例化的。这是我正在做的一个非常概括的版本:backgroundWorker2AutoResetEventbackgroundWorker2_RunWorkerCompleteResetFormControls()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。谢谢。

4

3 回答 3

5

RunWorkerCompleted 将在启动 BackgroundWorker 的线程上执行。由于您正在链接 BackgroundWorkers(从 1 开始 2),因此 2 的 RunWorkerCompleted 正在 1 的线程上执行,而不是在 UI 线程上执行。

您需要使用 Invoke 编组回 UI 线程,或者将 UI 更新移动到 1 的 RunWorkerCompleted。

我的建议是在更新 UI 时始终检查 InvokeRequired,这样您就不必担心它来自哪个线程。

于 2010-05-12T16:05:26.430 回答
1

BackgroundWorker 对象不能嵌套。如果可能的话,我建议使用 .NET 4.0 任务,因为它们会嵌套。

只有使用Nito.Async库中的 ActionDispatcher 之类的东西才能嵌套 BGW。

于 2010-05-12T16:04:59.917 回答
0

访问另一个线程中的控件永远不会是线程安全的。

您的问题是:“如何使用另一个线程访问由 UI 线程创建的控件?”

答案是调用您的控件。这是一个链接,您可以在其中看到允许它的代码示例。

这能满足您的需要吗?

于 2010-05-12T16:05:11.383 回答