Application.DoEvents()和多线程
首先将以下代码放到Button事件里面:
private void btnStart_Click(object sender, EventArgs e) { for (int q = 0; q < 100000; q++) { textBox1.Text = q.ToString(); } }
你会发现当点击Start按钮后,循环会一直进行,此时窗体会出现假死的状态,如:无法拖动。直到循环结束,textBox1中才会显示出结果。如何解决窗体的假死状态?修改以上代码如下:
private void btnStart_Click(object sender, EventArgs e) { for (int q = 0; q < 100000; q++) { textBox1.Text = q.ToString(); //实时响应文本框中的值 Application.DoEvents(); } }
此时再次点击Start按钮后,textBox中的数字会不断改变,同时,你也可以拖动窗体......

但是这样使用Application.DoEvents()好吗?如果用多线程来实现相同的效果呢?多线程实现代码如下:
public Form1() { InitializeComponent(); //不捕获跨线程调用引起的异常 CheckForIllegalCrossThreadCalls = false; } private void btnStart_Click(object sender, EventArgs e) { Thread s1 = new Thread(new ThreadStart(ThreadMethod)); s1.Start(); } public void ThreadMethod() { for (int q = 0; q < 100000; q++) { textBox1.Text = q.ToString(); } }
注意:Form1()里面添加了一行代码"CheckForIllegalCrossThreadCalls = false;",解决跨线程调用产生的异常。
以上代码和使用Application.DoEvents()达到相同的效果。
下面我们来比较一下,使用Application.DoEvents()和使用多线程哪个更耗时?代码如下:
public void ThreadMethod() { label1.Text = DateTime.Now.ToString(); for (int q = 0; q < 100000; q++) { textBox1.Text = q.ToString(); } label2.Text = DateTime.Now.ToString(); } private void btnStartDoEvent_Click(object sender, EventArgs e) { label3.Text = DateTime.Now.ToString(); for (int q = 0; q < 100000; q++) { textBox2.Text = q.ToString(); Application.DoEvents();//实时响应文本框中的值 } label4.Text = DateTime.Now.ToString(); }

(假设以上比较时间的代码处在正确的位置)由此可知,Application.DoEvents()消耗的时间更少,但这里并不建议使用Application.DoEvents(),因为它会引起很多未知的错误。
补充:由于显示的定义"CheckForIllegalCrossThreadCalls = false"并不是好的方法,下面来改进一下,如下:
int i; private void btnStart_Click(object sender, EventArgs e) { Thread s1 = new Thread(new ThreadStart(ThreadMethod)); s1.Start(); } private void ThreadMethod() { for (i = 0; i < 100000; i++) { MethodInvoker mi = new MethodInvoker(Count); this.Invoke(mi); //BeginInvoke(mi); } } private void Count() { textBox1.Text = i.ToString(); }
MethodInvoker实质上是一个委托,查看其定义可知......
using System; namespace System.Windows.Forms { // 表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。 public delegate void MethodInvoker(); }
以上用匿名委托的方式似乎更简洁,代码如下:
private void btnStart_Click(object sender, EventArgs e) { new Thread((ThreadStart)(delegate () { for (int i = 0; i < 10000; i++) { label1.Invoke((MethodInvoker)delegate () { textBox1.Text = i.ToString(); }); }; })).Start(); }
在次线程上计算,在主线程上调用label。
浙公网安备 33010602011771号