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

sve*_*vit 7 .net c# multithreading exception-handling winforms

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

我正在尝试侦听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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

}

Fre*_*örk 19

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用的是.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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这样的SetControlText方法可以更短(甚至是静态)(这在2.0和3.5中都适用):

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

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