什么是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 线程中的异步调用,用一个线程去处理一个耗时间的任务。
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(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(); });