代码之家  ›  专栏  ›  技术社区  ›  sventevit

侦听COM端口时跨线程操作无效[重复]

  •  6
  • sventevit  · 技术社区  · 17 年前

    可能重复:
    Getting Cross-thread operation not valid
    Cross-thread operation not valid

    我正在尝试侦听COM端口,以便为serialport.dataReceived事件创建新的处理程序。逻辑很简单——我写了一些东西到textbox 1,按下按钮1,我的文本应该在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();
            }
        }
    }
    

    }

    2 回复  |  直到 13 年前
        1
  •  19
  •   Fredrik Mörk    17 年前

    我猜是这样的 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方法中,这两者之间的任何性能差异都是可以忽略的,所以我倾向于使用较短的形式,仅仅是因为编写的代码更少。

        2
  •  1
  •   Steven Evers    17 年前

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

    (.NET 3.5)

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

    或 (.NET 2)

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

    编辑>有时对长时间运行的操作使用invoke仍然会挂起UI,使用begininvoke显然会异步执行该操作,而UI不会挂起。