侦听 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&lt;T1, T2&gt; 委托不可用,因此您必须定义自己的委托:

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'从一个>线程访问,而不是它在[重复]上创建的线程

跨线程Winforms控件编辑[重复]

跨线程操作无效:控件“statusStrip”从创建它的线程以外的线程访问

跨线程操作无效:控件从创建它的线程以外的线程访问