为了让程序尽快响应用户操作,在开发Windows应用程序时经常会使用到线程。对于耗时的操作如果不使用线程将会是UI界面长时间处于停滞状态,这种情况是用户非常不愿意看到的,在这种情况下我们希望使用线程来解决这个问题。
下面是一个使用多线程操作界面UI的代码:
InBlock.gifusing System;
InBlock.gifusing System.Collections.Generic;
InBlock.gifusing System.ComponentModel;
InBlock.gifusing System.Data;
InBlock.gifusing System.Drawing;
InBlock.gifusing System.Text;
InBlock.gifusing System.Windows.Forms;
InBlock.gifusing System.Threading;
InBlock.gif
InBlock.gifnamespace ThreadPoolDemo
InBlock.gif{
InBlock.gif        public partial class ThreadForm : Form
InBlock.gif        {
InBlock.gif                public ThreadForm()
InBlock.gif                {
InBlock.gif                        InitializeComponent();
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnThread_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        Thread thread = new Thread(new ThreadStart(Run));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif
InBlock.gif                private void Run()
InBlock.gif                {
InBlock.gif                        while (progressBar.Value < progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                progressBar.PerformStep();
InBlock.gif                        }
InBlock.gif                }
InBlock.gif        }
InBlock.gif}

程序的界面如下:
 
我们的本意是点击“启动”按钮来启动模拟一个操作,在进度条中显示操作的总体进度。不过如果我们真的点击“启动”按钮会很失望,因为它会抛出一个System.InvalidOperationException异常,异常描述就是“线程间操作无效: 从不是创建控件‘progressBar’的线程访问它。”

CheckForIllegalCrossThreadCalls属性
之所以会出现这样的情况是因为在.NET中做了限制,不允许在调试环境下使用线程访问并非它自己创建的UI控件,这么做可能是怕在多线程环境下对界面控件进行操作会出现不可预知的情况,如果开发者可以确认自己的代码操作界面不会出现问题,可以用比较简单的方法解决,那就是设置CheckForIllegalCrossThreadCalls这个静态属性,它默认是true,如果将其设为false的话,以后在多线程环境下操作界面也不会抛出异常了,我们上面的代码可以修改为:
InBlock.gifusing System;
InBlock.gifusing System.Collections.Generic;
InBlock.gifusing System.ComponentModel;
InBlock.gifusing System.Data;
InBlock.gifusing System.Drawing;
InBlock.gifusing System.Text;
InBlock.gifusing System.Windows.Forms;
InBlock.gifusing System.Threading;
InBlock.gif
InBlock.gifnamespace ThreadPoolDemo
InBlock.gif{
InBlock.gif        public partial class ThreadForm : Form
InBlock.gif        {
InBlock.gif                public ThreadForm()
InBlock.gif                {
InBlock.gif                        InitializeComponent();
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnThread_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        Thread thread = new Thread(new ThreadStart(Run));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif
InBlock.gif                private void Run()
InBlock.gif                {
InBlock.gif                        while (progressBar.Value < progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                progressBar.PerformStep();
InBlock.gif                        }
InBlock.gif                }
InBlock.gif        }
InBlock.gif}

这样再执行程序就不会抛出异常了。
不过使用上面的代码我们可能还有些犯嘀咕,毕竟是不允许直接在线程中直接操作界面的,那么我们还可以用Invoke方法。

Invoke方法来操作界面
下面是一个例子:
InBlock.gifusing System;
InBlock.gifusing System.Collections.Generic;
InBlock.gifusing System.ComponentModel;
InBlock.gifusing System.Data;
InBlock.gifusing System.Drawing;
InBlock.gifusing System.Linq;
InBlock.gifusing System.Text;
InBlock.gifusing System.Windows.Forms;
InBlock.gifusing System.Threading;
InBlock.gif
InBlock.gifnamespace ThreadPoolDemo
InBlock.gif{
InBlock.gif        public partial class ThreadForm : Form
InBlock.gif        {
InBlock.gif                //定义delegate以便Invoke时使用
InBlock.gif                private delegate void SetProgressBarValue(int value);
InBlock.gif                public ThreadForm()
InBlock.gif                {
InBlock.gif                        InitializeComponent();
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnThread_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程
InBlock.gif                        //CheckForIllegalCrossThreadCalls = false;
InBlock.gif                        Thread thread = new Thread(new ThreadStart(Run));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif                //使用线程来直接设置进度条
InBlock.gif                private void Run()
InBlock.gif                {
InBlock.gif                        while (progressBar.Value < progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                progressBar.PerformStep();
InBlock.gif                        }
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnInvoke_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        Thread thread = new Thread(new ThreadStart(RunWithInvoke));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif                //使用Invoke方法来设置进度条
InBlock.gif                private void RunWithInvoke()
InBlock.gif                {
InBlock.gif                        int value = progressBar.Value;
InBlock.gif                        while (value< progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                //如果是跨线程调用
InBlock.gif                                if (InvokeRequired)
InBlock.gif                                {
InBlock.gif                                        this.Invoke(new SetProgressBarValue(SetProgressValue), value++);
InBlock.gif                                }
InBlock.gif                                else
InBlock.gif                                {
InBlock.gif                                        progressBar.Value = ++value;
InBlock.gif                                }
InBlock.gif                        }
InBlock.gif                }
InBlock.gif                //跟SetProgressBarValue委托相匹配的方法
InBlock.gif                private void SetProgressValue(int value)
InBlock.gif                {
InBlock.gif                        progressBar.Value = value;
InBlock.gif                }
InBlock.gif        }
InBlock.gif}
InBlock.gif

这个方法的功能跟上面的操作是一样的,只不过不需要设置CheckForIllegalCrossThreadCalls属性,而且还不会抛出异常,当然除了上面的方法之外,还可以使用BackgroundWorker类来完成同样的功能。

BackgroundWorker类操作界面
因为使用BackgroundWorker类操作UI界面的例子周公博客上已经有过例子,所以这里的例子代码注释比较简单,读者可以看周公以前的示例,这次所使用的代码示例如下:
InBlock.gifusing System;
InBlock.gifusing System.Collections.Generic;
InBlock.gifusing System.ComponentModel;
InBlock.gifusing System.Data;
InBlock.gifusing System.Drawing;
InBlock.gifusing System.Linq;
InBlock.gifusing System.Text;
InBlock.gifusing System.Windows.Forms;
InBlock.gifusing System.Threading;
InBlock.gif
InBlock.gifnamespace ThreadPoolDemo
InBlock.gif{
InBlock.gif        public partial class ThreadForm : Form
InBlock.gif        {
InBlock.gif                //定义delegate以便Invoke时使用
InBlock.gif                private delegate void SetProgressBarValue(int value);
InBlock.gif                public ThreadForm()
InBlock.gif                {
InBlock.gif                        InitializeComponent();
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnThread_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程
InBlock.gif                        //CheckForIllegalCrossThreadCalls = false;
InBlock.gif                        Thread thread = new Thread(new ThreadStart(Run));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif                //使用线程来直接设置进度条
InBlock.gif                private void Run()
InBlock.gif                {
InBlock.gif                        while (progressBar.Value < progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                progressBar.PerformStep();
InBlock.gif                        }
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnInvoke_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        Thread thread = new Thread(new ThreadStart(RunWithInvoke));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif                //使用Invoke方法来设置进度条
InBlock.gif                private void RunWithInvoke()
InBlock.gif                {
InBlock.gif                        int value = progressBar.Value;
InBlock.gif                        while (value< progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                //如果是跨线程调用
InBlock.gif                                if (InvokeRequired)
InBlock.gif                                {
InBlock.gif                                        this.Invoke(new SetProgressBarValue(SetProgressValue), value++);
InBlock.gif                                }
InBlock.gif                                else
InBlock.gif                                {
InBlock.gif                                        progressBar.Value = ++value;
InBlock.gif                                }
InBlock.gif                        }
InBlock.gif                }
InBlock.gif                //跟SetProgressBarValue委托相匹配的方法
InBlock.gif                private void SetProgressValue(int value)
InBlock.gif                {
InBlock.gif                        progressBar.Value = value;
InBlock.gif                }
InBlock.gif        }
InBlock.gif}
InBlock.gif

 当然,除了BackgroundWorker可以完成上面的功能之外,利用System.Windows.Forms.Timer类也能完场上面的功能,代码如下:
InBlock.gifusing System;
InBlock.gifusing System.Collections.Generic;
InBlock.gifusing System.ComponentModel;
InBlock.gifusing System.Data;
InBlock.gifusing System.Drawing;
InBlock.gifusing System.Linq;
InBlock.gifusing System.Text;
InBlock.gifusing System.Windows.Forms;
InBlock.gifusing System.Threading;
InBlock.gif
InBlock.gifnamespace ThreadPoolDemo
InBlock.gif{
InBlock.gif        public partial class ThreadForm : Form
InBlock.gif        {
InBlock.gif                //定义delegate以便Invoke时使用
InBlock.gif                private delegate void SetProgressBarValue(int value);
InBlock.gif                private BackgroundWorker worker;
InBlock.gif                public ThreadForm()
InBlock.gif                {
InBlock.gif                        InitializeComponent();
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnThread_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        //指示是否对错误线程的调用,即是否允许在创建UI的线程之外访问线程
InBlock.gif                        //CheckForIllegalCrossThreadCalls = false;
InBlock.gif                        Thread thread = new Thread(new ThreadStart(Run));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif                //使用线程来直接设置进度条
InBlock.gif                private void Run()
InBlock.gif                {
InBlock.gif                        while (progressBar.Value < progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                progressBar.PerformStep();
InBlock.gif                        }
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnInvoke_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        Thread thread = new Thread(new ThreadStart(RunWithInvoke));
InBlock.gif                        thread.Start();
InBlock.gif                }
InBlock.gif                //使用Invoke方法来设置进度条
InBlock.gif                private void RunWithInvoke()
InBlock.gif                {
InBlock.gif                        int value = progressBar.Value;
InBlock.gif                        while (value< progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                //如果是跨线程调用
InBlock.gif                                if (InvokeRequired)
InBlock.gif                                {
InBlock.gif                                        this.Invoke(new SetProgressBarValue(SetProgressValue), value++);
InBlock.gif                                }
InBlock.gif                                else
InBlock.gif                                {
InBlock.gif                                        progressBar.Value = ++value;
InBlock.gif                                }
InBlock.gif                        }
InBlock.gif                }
InBlock.gif                //跟SetProgressBarValue委托相匹配的方法
InBlock.gif                private void SetProgressValue(int value)
InBlock.gif                {
InBlock.gif                        progressBar.Value = value;
InBlock.gif                }
InBlock.gif
InBlock.gif                private void btnBackgroundWorker_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        worker = new BackgroundWorker();
InBlock.gif                        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
InBlock.gif                        //当工作进度发生变化时执行的事件处理方法
InBlock.gif                        worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
InBlock.gif                        //当事件处理完毕后执行的方法
InBlock.gif                        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
InBlock.gif                        worker.WorkerReportsProgress = true;//支持报告进度更新
InBlock.gif                        worker.WorkerSupportsCancellation = false;//不支持异步取消
InBlock.gif                        worker.RunWorkerAsync();//启动执行
InBlock.gif                        btnBackgroundWorker.Enabled = false;
InBlock.gif                }
InBlock.gif                //当事件处理完毕后执行的方法
InBlock.gif                void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
InBlock.gif                {
InBlock.gif                        btnBackgroundWorker.Enabled=true;
InBlock.gif                }
InBlock.gif                //当工作进度发生变化时执行的事件处理方法
InBlock.gif                void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
InBlock.gif                {
InBlock.gif                        //可以在这个方法中与界面进行通讯
InBlock.gif                        progressBar.Value = e.ProgressPercentage;
InBlock.gif                }
InBlock.gif                //开始启动工作时执行的事件处理方法
InBlock.gif                void worker_DoWork(object sender, DoWorkEventArgs e)
InBlock.gif                {
InBlock.gif                        int value = progressBar.Value;
InBlock.gif                        while (value < progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                worker.ReportProgress(++value);//汇报进度
InBlock.gif                        }
InBlock.gif                }
InBlock.gif                //使用System.Windows.Forms.Timer来操作界面能
InBlock.gif                private void btnTimer_Click(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        progressBar.Value = 0;
InBlock.gif                        //注意在.net中有多个命名空间下存在Timer类,为了便于区别,使用了带命名空间形式
InBlock.gif                        System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
InBlock.gif                        timer.Interval = 1;
InBlock.gif                        timer.Tick += new EventHandler(timer_Tick);
InBlock.gif                        timer.Enabled = true;
InBlock.gif                }
InBlock.gif                //Timer中要定期执行的方法
InBlock.gif                void timer_Tick(object sender, EventArgs e)
InBlock.gif                {
InBlock.gif                        int value = progressBar.Value;
InBlock.gif                        if (value < progressBar.Maximum)
InBlock.gif                        {
InBlock.gif                                progressBar.Value = value+100;
InBlock.gif                        }
InBlock.gif                }
InBlock.gif        }
InBlock.gif}
InBlock.gif
总结:本篇主要讲述了使用线程操作Windows应用程序界面的方法,这些方法在编写多线程的UI程序时可以参考。
周公
2010-01-11