多线程与UI操作
什么是UI线程?
所有的 .NET Framework 应用程序都是使用单线程创建的,单线程用于执行该应用程序, 在.net winform开发中,这样的线程创建并管理用户界面 (UI),因而称为 UI 线程。
什么是UI假死?
当winfrom程序在执行一个长时间耗时的任务时,应用程序出现不能点击、移动,看起来是程序已经死掉的现象(其实还在跑);
展开
private void add()
{
int i = 0;
while (true)
{
i++;
this.label1.Text = i.ToString();
Thread.Sleep(1000);
}
}
private void button1_Click(object sender, EventArgs e)
{
add();
}
上面应用程序目的是在界面显示一个数的累加1的过程,当点击button时,程序就出现了假死的现象.
怎么解决?
解决的方法就是使用 UI 线程中的异步调用,用一个线程去处理一个耗时间的任务。
View Code
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(add));
thread.Start();
}
修改button_click事件,创建一个线程去处理add这个方法。
修改后再运行时,会报以下错误
{"线程间操作无效: 从不是创建控件“label1”的线程访问它。"}
这是因为微软为了线程安全,不是UI线程的不能去处理界面。处理方法就是使用控件的 InvokeRequired 属性判断是否跨线程操作,如果需要则使用 Invoke 方法回调形式操作。
改进代码
1 private void add()
2 {
3 int i = 0;
4 while (true)
5 {
6 i++;
7 if (label1.InvokeRequired)
8 {
9 label1.Invoke(new Action<string>(write), i.ToString());
10 }
11 else
12 {
13 label1.Text = i.ToString();
14 }
15 this.label1.Text = i.ToString();
16 Thread.Sleep(1000);
17 }
18 }
19
20 private void write(string str)
21 {
22
23 label1.Text = str;
24 }
现在运行程序时, 应用程序就不会出现假死的情况了。当然这不是很好的解决方法,如果要修改多个控件的话,就要新建多个像write那样的方法了,这样代码反而不好阅读了。 解决方式就是用匿名方法,开始动手
修改
label1.Invoke(delegate() { this.label1.Text = i.ToString(); });
编译,还是会报错,无法将 匿名方法 转换为类型“System.Delegate”,因为它不是委托类型 ,这是神马情况 ,google一下原因后,原来Invoke第一个参数接受的是delegate类型,而不是具体的委托类型,所以CLR无法知道是哪种委托,继续修改,将delegate转换为具体的委托
label1.Invoke((MethodInvoker)delegate() { this.label1.Text = i.ToString(); });