三种跨线程控件访问方法
1、不安全的方法: 将 Control.CheckForIllegalCrossThreadCalls 设置为false (.net1.0中没有)
2、安全的方法: 异步委托(不要将除了控件访问外其他逻辑代码放到委托的回调方法中)
private void button1_Click(object sender, System.EventArgs e)
{
//实例化一个线程,并引用线程开始执行时要调用的方法
//这个方法在线程调用start()方法时启动
barProgress = new Thread(new ThreadStart(ProgressProc)); //调用线程启动ProgressProc方法
barProgress.IsBackground = true;
barProgress.Start();
}
private void ProgressProc()
{
MethodInvoker mi = new MethodInvoker(UpdateProgress);//MethodInvoker表示一个委托,该委托可以执行托管代码中声明为
while (true)
{
Invoke(mi);
int sleepTime = this.waitTime;
Thread.Sleep(sleepTime);
}
}
private void UpdateProgress() //实现UpdateProgress
{
//更新界面
}
3、安全的方法: 就是使用BackgroundWorker来替代你自己创建的线程(.net1.0中没有)
BackgroundWorker是一个在System.ComponentModel命名空间下帮助类,它管理着工作线程。它提供了以下特性:
- "cancel" 标记,对于给工作线程打信号让它结束而没有使用 Abort的情况
- 提供报道进度,完成度和退出的标准方案
- 实现了IComponent接口,允许它参与Visual Studio设计器
- 在工作线程之上做异常处理
- 更新Windows Forms控件以应答工作进度或完成度的能力
最后两个特性是相当地有用:意味着你不再需要将try/catch语句块放到你的工作线程中了,并且更新Windows Forms控件不需要调用 Control.Invoke了。
BackgroundWorker使用线程池工作,对于每个新任务,它循环使用避免线程们得到休息。这意味着你不能在 BackgroundWorker线程上调用 Abort了。
下面是使用BackgroundWorker最少的步骤:
- 实例化 BackgroundWorker,为DoWork事件增加委托。
- 调用RunWorkerAsync方法,使用一个随便的object参数。
这就设置好了它,任何被传入RunWorkerAsync的参数将通过事件参数的Argument属性,传到DoWork事件委托的方法中,下面是例子:
class Program {
static BackgroundWorker bw = new BackgroundWorker();
static void Main() {
bw.DoWork += bw_DoWork;
bw.RunWorkerAsync ("Message to worker");
Console.ReadLine();
}
static void bw_DoWork (object sender, DoWorkEventArgs e)
{
// 这被工作线程调用
Console.WriteLine (e.Argument); // 写"Message to worker"
// 执行耗时的任务...
}
BackgroundWorker也提供了RunWorkerCompleted事件,它在DoWork事件完成后触发,处理RunWorkerCompleted事件并不是强制的,但是为了查询到DoWork中的异常,你通常会这么做的。RunWorkerCompleted中的代码可以更新Windows Forms 控件,而不用显示的信号编组,而DoWork中就可以这么做。
添加进程报告支持:
- 设置WorkerReportsProgress属性为true
- 在DoWork中使用“完成百分比”周期地调用ReportProgress方法,以及可选用户状态对象
- 处理ProgressChanged事件,查询它的事件参数的 ProgressPercentage属性
ProgressChanged中的代码就像RunWorkerCompleted一样可以自由地与UI控件进行交互,这在更性进度栏尤为有用。
添加退出报告支持:
- 设置WorkerSupportsCancellation属性为true
- 在DoWork中周期地检查CancellationPending属性:如果为true,就设置事件参数的Cancel属性为true,然后返回。(工作线程可能会设置Cancel为true,并且不通过CancellationPending进行提示——如果判定工作太过困难并且它不能继续运行)
- 调用CancelAsync来请求退出
下面的例子实现了上面描述的特性:
Code