侦听COM端口时跨线程操作无效

可能重复:
使跨线程操作无效
跨线程操作无效

我正在尝试侦听COM端口,以便为SerialPort.DataReceived事件创建新的处理程序。 逻辑很简单 – 我写了一些东西给TextBox1,按下Button1,我的文本应该在Label1中显示它自己。 但我的应用程序不想运行,因为它抛出“交叉线程操作无效”错误。 我做了一些搜索并找到了Invoke对象 – 我如何在我的例子中使用它? 为什么我需要包含Invoke逻辑?

namespace WindowsApplication1 { public partial class Form1 : Form { SerialPort sp = new SerialPort(); public Form1() { InitializeComponent(); sp.DataReceived += MyDataReceivedHandler; } private void Form1_Load(object sender, EventArgs e) { } private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { try { //sp.PortName = "COM3"; //sp.Open(); Label1.Text = sp.ReadLine(); } catch (Exception exception) { RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; } finally { sp.Close(); } } private void button1_Click(object sender, EventArgs e) { try { sp.PortName = "COM3"; sp.Open(); sp.WriteLine(TextBox1.Text); } catch (Exception exception) { RichTextBox1.Text = exception.Message + "\n\n" + exception.Data; } finally { sp.Close(); } } } 

}

我的猜测是MyDataReceivedHandler运行在与GUI不同的线程上。 为了解决这个问题,您需要在正确的线程上调用Text setter。 这是这样做的一个例子:

 public void SetControlText(Control control, string text) { if (this.InvokeRequired) { this.Invoke(new Action(SetControlText), new object[] { control, text }); } else { control.Text = text; } } private void MyDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) { try { //sp.PortName = "COM3"; //sp.Open(); SetControlText(Label1, sp.ReadLine()); } catch (Exception exception) { SetControlText(RichTextBox1, exception.Message + "\n\n" + exception.Data); } finally { sp.Close(); } } 

如果您使用的是.NET Framework 2.0,则上述Action委托不可用,因此您必须定义自己的委托:

 private delegate void SetControlTextHandler(Control control, string text); public void SetControlText(Control control, string text) { if (this.InvokeRequired) { this.Invoke(new SetControlTextHandler(SetControlText), new object[] { control, text }); } else { control.Text = text; } } 

SetControlText方法可以像这样更短(甚至是静态)(这适用于2.0和3.5):

 public static void SetControlText(Control control, string text) { ´control.Invoke((MethodInvoker)delegate { control.Text = text; }); } 

然后,您不需要每次都检查InvokeRequired ,但另一方面,即使不需要,您也将在委托中包装调用。 我认为在这样的GUI方法中,这两者之间的任何性能差异都是可以忽略的,所以我倾向于使用较短的forms,因为它写的代码较少。

每当从与其创建的线程不同的线程访问UI控件时,您还可以执行以下操作:

(.NET 3.5)

 myControl.BeginInvoke(new MethodInvoker( () => myControl.whatever = whatever; )); 

或(.NET 2.0)

 myControl.BeginInvoke(new MethodInvoker( delegate { myControl.whatever = whatever; )); 

编辑>有时使用Invoke进行长时间运行操作可以/仍将挂起ui,使用BeginInvoke显然异步执行该操作,并且ui不会挂起。