侦听 COM 端口时跨线程操作无效[重复]
Posted
技术标签:
【中文标题】侦听 COM 端口时跨线程操作无效[重复]【英文标题】:Cross-thread operation not valid while listening to a COM port [duplicate] 【发布时间】:2010-11-25 12:56:46 【问题描述】:可能重复:Getting Cross-thread operation not validCross-thread operation not valid
我正在尝试监听 COM 端口,以便为 SerialPort.DataReceived 事件创建新的处理程序。逻辑很简单——我在 TextBox1 中写了一些东西,按下 Button1,我的文本应该在 Label1 中显示出来。但是我的应用程序不想运行,因为它会引发“跨线程操作无效”错误。 我进行了一些搜索并找到了 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();
【问题讨论】:
@Peter:这里的COM口是RS232串口。尽管 USB 和所有设备(例如 GPS、医疗)仍然使用串行端口进行 PC 通信。 @_simon_:只是好奇:这个特定应用程序中使用的 COM 端口是什么? @_simon_:我更新了答案 【参考方案1】:我的猜测是MyDataReceivedHandler
运行在与 GUI 不同的线程上。为了解决这个问题,您需要在正确的线程上调用 Text
设置器。这是这样做的一个示例:
public void SetControlText(Control control, string text)
if (this.InvokeRequired)
this.Invoke(new Action<Control,string>(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<T1, T2>
委托不可用,因此您必须定义自己的委托:
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 方法中,这两者之间的任何性能差异都可以忽略不计,因此我倾向于使用较短的形式,仅仅是因为编写的代码更少。
【讨论】:
看来,这仅适用于 3.5。我使用 Visual Studio 2005,现在我安装了 3.5 SP1。我可以在 Visual Studio 2005 的哪个位置设置我使用的 .NET 框架? @_simon_:我已经用 2.0 兼容版本更新了答案 注意:如果委托中执行的操作长时间运行,它仍然可以阻塞UI,因为调用只会导致UI线程处理该操作。在所有实现它的控件上使用 BeginInvoke 将异步执行操作,不会阻塞。 +1 你不应该在控件上调用invoke/begininvoke,而不是在'this'(表单)上调用吗?【参考方案2】:您还可以在从与创建它的线程不同的线程访问 UI 控件时执行以下操作:
(.NET 3.5)
myControl.BeginInvoke(new MethodInvoker( () => myControl.whatever = whatever; ));
或 (.NET 2.0)
myControl.BeginInvoke(new MethodInvoker( delegate myControl.whatever = whatever; ));
edit> 有时使用 Invoke 进行长时间运行的操作可以/仍然会挂起 ui,使用 BeginInvoke 显然会异步执行该操作,并且 ui 不会挂起。
【讨论】:
以上是关于侦听 COM 端口时跨线程操作无效[重复]的主要内容,如果未能解决你的问题,请参考以下文章
例外:跨线程操作无效:控制'pgImportProcess(进度条)'从一个线程访问,而不是它在[重复]上创建的线程
跨线程操作无效:控件'listBox1'从一个>线程访问,而不是它在[重复]上创建的线程