using System;
using System.IO.Ports;
using System.Windows.Forms;
using System.Threading;
public class SerialPortForm : Form
{
private SerialPort _serialPort;
private TextBox _textBox;
public SerialPortForm()
{
_textBox = new TextBox { Multiline = true, Dock = DockStyle.Fill };
Controls.Add(_textBox);
_serialPort = new SerialPort("COM1", 9600);
_serialPort.DataReceived += SerialPort_DataReceived;
_serialPort.Open();
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = _serialPort.ReadExisting();
UpdateTextBox(data);
}
private void UpdateTextBox(string text)
{
if (_textBox.InvokeRequired)
{
_textBox.BeginInvoke(new Action<string>(UpdateTextBox), text);
}
else
{
_textBox.AppendText(text);
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
_serialPort.Close();
base.OnFormClosing(e);
}
[STAThread]
static void Main()
{
Application.Run(new SerialPortForm());
}
}
Invoke和BeginInvoke是WinForms中用于跨线程更新UI的核心方法,主要区别体现在执行方式和线程行为上:
执行方式差异
Invoke:同步调用,会阻塞当前线程直到UI线程完成委托执行 BeginInvoke:异步调用,立即将委托加入UI线程消息队列后立即返回,不阻塞当前线程
线程行为对比
特性 Invoke BeginInvoke 线程阻塞 调用线程被阻塞直到UI操作完成 调用线程继续执行不等待 异常处理 异常直接抛给调用线程 异常需通过EndInvoke捕获 返回值 直接返回委托结果 返回IAsyncResult对象
典型应用场景
Invoke适用于需要确保UI更新完成后才能继续执行的逻辑,如串口数据接收后必须立即显示的场合 BeginInvoke更适合高频UI更新(如实时曲线绘制),避免因同步等待导致数据堆积
示例代码演示两种调用方式:
csharp Copy Code // 同步调用示例 _textBox.Invoke(new Action(s => _textBox.Text = s), "Text1");
// 异步调用示例 var asyncResult = _textBox.BeginInvoke(new Action(s => _textBox.Text = s), "Text2"); _textBox.EndInvoke(asyncResult); // 可选结束调用
实际开发中,高频数据场景建议优先使用BeginInvoke以避免界面卡顿,而需要严格顺序执行的逻辑则应选择Invoke。两者均通过检查InvokeRequired属性确保线程安全。
using System;
using System.Windows.Forms;
using System.IO.Ports;
public class SerialPortForm : Form
{
private TextBox _textBox;
private SerialPort _serialPort;
public SerialPortForm()
{
_textBox = new TextBox { Multiline = true, Dock = DockStyle.Fill };
Controls.Add(_textBox);
_serialPort = new SerialPort("COM1", 9600);
_serialPort.DataReceived += SerialPort_DataReceived;
_serialPort.Open();
}
private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string data = _serialPort.ReadExisting();
IAsyncResult result = _textBox.BeginInvoke(new Action<string>(UpdateTextBox), data);
// 如果需要等待UI更新完成(非必须)
_textBox.EndInvoke(result); // 这里会阻塞当前线程直到UI操作完成
}
private void UpdateTextBox(string text)
{
_textBox.AppendText(text);
}
protected override void OnFormClosing(FormClosingEventArgs e)
{
_serialPort.Close();
base.OnFormClosing(e);
}
[STAThread]
static void Main()
{
Application.Run(new SerialPortForm());
}
}