net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生,有一种方法是禁止编译器对跨线程访问作检查,Control.CheckForIllegalCrossThreadCalls = false;可以实现访问,但是出不出错不敢保证C#跨线程访问控件运行时错误,使用MethodInvoker即可解决。
C#中的Invoke方法主要用于在多线程环境下跨线程调用控件方法或者委托。它的作用是确保方法在正确的线程上执行,避免出现线程安全问题。
在访问控件是先判断控件的InvokeRequired属性,如果为true,则需要使用Invoke方法;如果为false,则可以直接调用方法。
在需要跨线程调用的地方,使用Invoke方法来执行指定的委托。
csharp
delegateShowMsg ShowSendMsg;
Action<Byte[]> ShowReceiveMsg;
private void frmMain_Load(object sender, EventArgs e)
{
ShowSendMsg = new delegateShowMsg(SendMsg_Show);
ShowReceiveMsg = new Action<byte[]>(ReceiveMsg_Show);
}
/// <summary>
/// 发送内容显示
/// </summary>
/// <param name="buf"></param>
private void SendMsg_Show(Byte[] buf)
{
if (txtSendMsg.InvokeRequired)
{
// 当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
this.txtSendMsg.Invoke(ShowSendMsg, buf);
}
else
{
this.txtSendMsg.Text += Change16ToStr(buf);
}
}
/// <summary>
/// 接收内容显示
/// </summary>
/// <param name="buf"></param>
private void ReceiveMsg_Show(Byte[] buf)
{
if (txtReceiveMsg.InvokeRequired)
{
// 当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
this.txtReceiveMsg.Invoke(ShowReceiveMsg, buf);
}
else
{
this.txtReceiveMsg.Text += Change16ToStr(buf);
}
}
/// <summary>
/// 串口数据接收事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
int n = serialPort.BytesToRead; //记录下数据长度
byte[] BUF_receive = new byte[n];
serialPort.Read(BUF_receive, 0, n); //读取缓存数据
ShowReceiveMsg(BUF_receive);
}
在上面的示例代码中,SendMsg_Show函数中通过判断txtSendMsg控件的InvokeRequired属性来确定是否需要使用Invoke方法。如果需要跨线程调用,就使用Invoke方法来执行委托;否则直接更新控件内容。
也可以直接用虚方法的方式来做,不要额外定义委托对象
csharp
private void serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
int n = serialPort.BytesToRead; //记录下数据长度
byte[] BUF_receive = new byte[n];
serialPort.Read(BUF_receive, 0, n); //读取缓存数据
ReceiveMsg_Show(BUF_receive);
}
private void ReceiveMsg_Show(Byte[] buf)
{
// 判断是否需要跨线程调用
if (txtReceiveMsg.InvokeRequired)
{
this.Invoke(new MethodInvoker(delegate { ReceiveMsg_Show(buf); }));
}
else
{
// 直接调用方法
this.txtReceiveMsg.Text += Change16ToStr(buf);
}
}
csharp
this.Invoke((EventHandler)(delegate
{
this.txtSendMsg.Text = Change16ToStr(buf);
}));
也可以直接在后台线程中通过这种方式直接访问控件