跨线程操作无效:控制'textBox1'从其创建的线程以外的线程访问

Fat*_*hra 169 c# multithreading invoke uart

我想从微控制器发送温度值,使用UART到C#接口和显示温度开启Label.Content.这是我的微控制器代码:

while(1) {
   key_scan(); // get value of temp
   if (Usart_Data_Ready())
   {
      while(temperature[i]!=0)
      {
         if(temperature[i]!=' ')
         {
            Usart_Write(temperature[i]);
            Delay_ms(1000);
         }
         i = i + 1;
      }
      i =0;
      Delay_ms(2000);
   }
}
Run Code Online (Sandbox Code Playgroud)

我的C#代码是:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
   txt += serialPort1.ReadExisting().ToString();
   textBox1.Text = txt.ToString();
}
Run Code Online (Sandbox Code Playgroud)

但是异常出现了" 跨线程操作无效:控制'textBox1'从一个线程访问,而不是在它创建的线程上 "请告诉我如何从我的微控制器获取温度字符串并删除此错误!

Mag*_*son 293

您的serialPort1_DataReceived方法中收到的数据来自另一个线程上下文而不是UI线程,这就是您看到此错误的原因.
要解决此问题,您必须使用MSDN文章中描述的调度程序:
如何:对Windows窗体控件进行线程安全调用

因此,不要直接在serialport1_DataReceived方法中设置text属性,而是使用以下模式:

delegate void SetTextCallback(string text);

private void SetText(string text)
{
  // InvokeRequired required compares the thread ID of the
  // calling thread to the thread ID of the creating thread.
  // If these threads are different, it returns true.
  if (this.textBox1.InvokeRequired)
  { 
    SetTextCallback d = new SetTextCallback(SetText);
    this.Invoke(d, new object[] { text });
  }
  else
  {
    this.textBox1.Text = text;
  }
}
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
  txt += serialPort1.ReadExisting().ToString();
  SetText(txt.ToString());
}
Run Code Online (Sandbox Code Playgroud)

  • `control.BeginInvoke` 也能工作吗?解决方案也可以是这样的 1 行,对吗?`textbox1.BeginInvoke((MethodInvoker)delegate(){ textbox1.Text = txt.ToString(); });` (3认同)

Thu*_*der 46

我不知道这是否足够好,但我确实创建了一个静态ThreadHelperClass类并将其实现如下.现在我可以轻松设置各种控件的文本属性而无需太多编码.

public static class ThreadHelperClass
    {
        delegate void SetTextCallback(Form f, Control ctrl, string text);
        /// <summary>
        /// Set text property of various controls
        /// </summary>
        /// <param name="form">The calling form</param>
        /// <param name="ctrl"></param>
        /// <param name="text"></param>
        public static void SetText(Form form, Control ctrl, string text)
        {
            // InvokeRequired required compares the thread ID of the 
            // calling thread to the thread ID of the creating thread. 
            // If these threads are different, it returns true. 
            if (ctrl.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                form.Invoke(d, new object[] { form, ctrl, text });
            }
            else
            {
                ctrl.Text = text;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

使用代码:

 private void btnTestThread_Click(object sender, EventArgs e)
        {
            Thread demoThread =
               new Thread(new ThreadStart(this.ThreadProcSafe));
            demoThread.Start();
        }

        // This method is executed on the worker thread and makes 
        // a thread-safe call on the TextBox control. 
        private void ThreadProcSafe()
        {
            ThreadHelperClass.SetText(this, textBox1, "This text was set safely.");
            ThreadHelperClass.SetText(this, textBox2, "another text was set safely.");
        }
Run Code Online (Sandbox Code Playgroud)

  • "这是一个很棒的解决方案",最令人敬畏的是"它是开放的扩展和通用".你可以随意添加新的UI更新功能,谢谢 (5认同)
  • 好东西!如果您需要读取文本: delegate string GetTextCallback(Form f, Control ctrl); public static string GetText(Form form, Control ctrl) { string text; if (ctrl.InvokeRequired) { GetTextCallback d = new GetTextCallback(GetText); 文本 = (字符串) (form.Invoke(d, new object[] { form, ctrl})); } else { 文本 = ctrl.Text; 返回文本;} } (2认同)

Hfo*_*ham 31

你可以简单地做到这一点.

TextBox.CheckForIllegalCrossThreadCalls = false;
Run Code Online (Sandbox Code Playgroud)

  • 错误的想法,因为当你进行'发布'编译时错误会回来. (24认同)
  • 但是培训多线程编程的好主意 (10认同)
  • @DerfSkren关心进一步解释?^ (2认同)
  • @EricWu设置该标志具有相同的效果,无论您是调试还是发布版本.被警告实际上是安全的,因而被迫在你创造的每一个GUI上重新发明轮子的"问题"都不会回来. (2认同)

Mig*_*ras 22

使用以下扩展名,只需传递以下操作:

_frmx.PerformSafely(() => _frmx.Show());
_frmx.PerformSafely(() => _frmx.Location = new Point(x,y));
Run Code Online (Sandbox Code Playgroud)

扩展类:

public static class CrossThreadExtensions
{
    public static void PerformSafely(this Control target, Action action)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action);
        }
        else
        {
            action();
        }
    }

    public static void PerformSafely<T1>(this Control target, Action<T1> action,T1 parameter)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, parameter);
        }
        else
        {
            action(parameter);
        }
    }

    public static void PerformSafely<T1,T2>(this Control target, Action<T1,T2> action, T1 p1,T2 p2)
    {
        if (target.InvokeRequired)
        {
            target.Invoke(action, p1,p2);
        }
        else
        {
            action(p1,p2);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mik*_*ike 15

沿着与之前的答案相同的行,但是非常短的添加允许使用所有Control属性而不具有跨线程调用异常.

辅助方法

    /// <summary>
    /// Helper method to determin if invoke required, if so will rerun method on correct thread.
    /// if not do nothing.
    /// </summary>
    /// <param name="c">Control that might require invoking</param>
    /// <param name="a">action to preform on control thread if so.</param>
    /// <returns>true if invoke required</returns>
    public bool ControlInvokeRequired(Control c,Action a)
    {
        if (c.InvokeRequired) c.Invoke(new MethodInvoker(delegate { a(); }));
        else return false;

        return true;
    }
Run Code Online (Sandbox Code Playgroud)

样本用法

    // usage on textbox
    public void UpdateTextBox1(String text)
    {
        //Check if invoke requied if so return - as i will be recalled in correct thread
        if (ControlInvokeRequired(textBox1, () => UpdateTextBox1(text))) return;
        textBox1.Text = ellapsed;
    }

    //Or any control
    public void UpdateControl(Color c,String s)
    {
        //Check if invoke requied if so return - as i will be recalled in correct thread
        if (ControlInvokeRequired(myControl, () => UpdateControl(c,s))) return;
        myControl.Text = s;
        myControl.BackColor = c;
    }
Run Code Online (Sandbox Code Playgroud)


rot*_*tor 7

使用共享容器在线程之间传输数据.