线程调用不一致

时间:2016-07-20 12:35:18

标签: c# multithreading

我是高级编程语言的初学者。我正在尝试使用VS 2010 C#

为串口创建一个WForms应用程序

我收到以下错误:

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

这发生在这里:

private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
    {
         recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.

        // Show in the terminal window 
        rtxtDataArea.ForeColor = Color.Green;    // error , 
        rtxtDataArea.AppendText(recievedData + "\n");
    }

我在收到一些数据时尝试更改文本框的颜色。 它会触发交叉线程错误。

问题是当我尝试更改标签的颜色时,为什么它不会在此处触发相同的错误?

private void btnConnect_Click(object sender, EventArgs e)
    {
        if (ComPort.IsOpen)
        {
            disconnect();
        }
        else
        {
            connect();
            rdText.ForeColor = Color.Blue;//ok, it works
        }
    }

这工作;第一个没有。

为什么呢? ComPort_DataReceived_1与btnConnect_Click的性质不同吗? 或者是什么原因?

我已经阅读了很多关于线程的内容,但我理解我无法使用,有人可以给出一个直观的解释吗?

2 个答案:

答案 0 :(得分:2)

在winforms中,只有一个线程可以更改UI上的任何内容,例如启用按钮,更改文本框等。通常这是UI线程。这通常是你唯一的主题。

但是,如果您启动一个新线程,该线程可能想要更改UI。如果此新线程触发表单收到的事件,则会发生这种情况。

每当您看到上创建的线程以外的线程访问的消息时,您几乎可以肯定是这种情况。

解决此问题的最简单方法是使用函数 Control.IsInvokeRequired Control.Invoke 。这样做的模式如下。以下函数更新myForm上的myButton

private void UpdateMyButton (MyType myParameter)
{
    if (myButton.InvokeRequired)
    {   // this is not the thread that created the button to update
        this.Invoke(new MethodInvoker( () => this.UpdateMyButton(myParameter)));
        // this will let the UI thread call this function with the same parameter.
    }
    else
    {   // Now called by the UI thread: this thread may change myButton
        myButton.Enabled = myParameter.ShouldButtonEnable;
        myButton.Text = myParameter.ButtonText;
    }
}

顺便说一句,如果你必须更新表单上的几个控件,你应该为每个控件检查InvokeRequired。但是,由于它们通常由相同的UI线程创建,因此检查this.InvokeRequired就足够了。

Control.Invoke在调用完成后返回,因此在更新所有项目之后。返回Invoke后,您可以使用UpdateMyButton的结果。

如果您不希望非ui线程等待UpdateMyButton的完成,请考虑使用Control.BeginInvoke:"嘿UI线程,只要您有时间,是否可以我的UpdateMyButton。当然,在这种情况下,您无法使用UpdateMyButton的结果

答案 1 :(得分:1)

因为“DataReceived”在另一个线程而不是UI线程上运行。您必须使用Invoke:

private void ComPort_DataReceived_1(object sender, SerialDataReceivedEventArgs e)
{
  recievedData = ComPort.ReadExisting(); //read all available data in the receiving buffer.

  if (InvokeRequired)
  {
    // If not on UI thread, then invoke
    Invoke(new MethodInvoker(() =>
    {
      // Show in the terminal window 
      rtxtDataArea.ForeColor = Color.Green; // error , 
      rtxtDataArea.AppendText(recievedData + "\n");
    }));
  }
  else
  {
    // On UI thread, invoke not needed
    // Show in the terminal window 
    rtxtDataArea.ForeColor = Color.Green; // error , 
    rtxtDataArea.AppendText(recievedData + "\n");
  }
}