1、通过SynchronizationContext的Post/Send方法
主要原理是:在线程执行过程中,需要更新到UI控件上的数据不再直接更新,而是通过UI线程上下文的Post/Send方法,将数据以异步/同步消息的形式发送到UI线程的消息队列;UI线程收到该消息后,根据消息是异步消息还是同步消息来决定通过异步/同步的方式调用SetLabel1Post方法直接更新自己的控件了。
从本质讲,向UI线程发送的消息并是不简单数据,而是一条委托调用命令。
1.1、获取UI线程同步上下文(在窗体构造函数或FormLoad事件中)
SynchronizationContext synchronizationContext = null;
System.Timers.Timer timer = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
synchronizationContext = SynchronizationContext.Current;
timer.Interval += 100;
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
1.2、定义更新UI控件的方法
private void SetLabel1Post(object text)
{
this.label1.Text = text.ToString();
}
1.3、在定时线程中调用更新UI控件的方法
Boolean flag = false;
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
string str;
if (flag)
{
str = "数据回读值:0x0001";
flag = false;
}
else
{
str = "寄存器回读值:0x0088";
flag = true;
}
synchronizationContext.Post(SetLabel1Post, str);
}
2、通过Invoke/BeginInvoke方法
本质上还是把线程中要提交的消息,通过控件句柄调用委托交到UI线程中去处理。
2.1、定义定时线程
System.Timers.Timer timer = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
timer.Interval += 100;
timer.Elapsed += Timer_Elapsed;
timer.Start();
}
2.2、采用跨线程委托
private void SetLabelText(Label label,string text)
{
try
{
if (label.InvokeRequired)
{
MethodInvoker mi = delegate ()
{
label.Text = text;
};
label.Invoke(mi);
}
else
{
label.Text = text;
}
}catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
2.3、在定时处理函数更新控件
Boolean flag = false;
private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
string str;
if (flag)
{
str = "数据回读值:0x0001";
flag = false;
}
else
{
str = "寄存器回读值:0x0086";
flag = true;
}
SetLabelText(label2, str);
}
2.4、解决出现“访问已释放句柄“的异常的方法
while (!this.label3.IsHandleCreated)
{
if (this.label3.Disposing || this.label3.IsDisposed)
return;
}
3、通过设置窗体属性,取消非法跨线程调用检查来避免"线程间操作无效异常"
通过设置该属性为false简单的屏蔽了该异常(非线程安全,建议不使用)。
public Form1()
{
InitializeComponent();
//取消非法跨线程调用检查
Control.CheckForIllegalCrossThreadCalls = false;
}