深入浅出多线程系列之六:事件驱动异步模式(EAP,WebClient,BackgroundWorker)

Event-based asynchronous(EAP)在多线程的环境中提供了一个简单的处理方式。
它有以下几个特性:
  1. 支持取消。
  2. 可以安全的更新WPF或windows Forms 控件。
  3. 在completion event中可以查询异常信息。
  4. “在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序。
  5. 同时执行多个操作,每个操作完成时都会接到通知。
  6. 等待资源变得可用,但不会停止(“挂起”)您的应用程序。
  7. 使用熟悉的事件和委托模型与挂起的异步操作通信。
EAP仅仅只是一个模式而已。,所以这些特性必须都由实现者来实现。在Framework中有少数几个类支持这种模式,最著名的就是BackgroundWorker和System.Net.WebClient 了。
这个模式的本质是:每个类都提供了一些相似的成员来管理多线程,例如:
public byte[] DownloadData(Uri address);
public void DownloadDataAsync(Uri address);
public void DownloadDataAsync(Uri address, object userToken);
public event DownloadDataCompletedEventHandler DownloadDataCompleted;


public void CancelAsync(); //取消操作
public bool IsBusy { get; } //获取是否正在运行的信息。

下面是使用WebClient 的例子:
var wc = new WebClient(); wc.DownloadStringCompleted += (sender, args) => { if (args.Cancelled) Console.WriteLine("Canceled"); else if (args.Error != null) Console.WriteLine("Exception:" + args.Error.Message); else { Console.WriteLine(args.Result.Length + " chars were downloaded"); } }; wc.DownloadStringAsync(new Uri("http://www.cnblogs.com/LoveJenny/"));
一个WebClient虽然有多个异步方法,但是因为它们都共享了相同的CancelAsync 和IsBusy属性,所以一次只能有一个异步操作。
BackgroundWorker
BackgroundWorker是System.ComponentModel下面的一个管理工作线程的帮助类,提供了下面几个特性
  1. 支持取消。
  2. 可以安全的更新WPF或windows Forms 控件。
  3. 在completion event中可以查询异常信息。
  4. 可以报告进度。
  5. 因为实现了IComponent接口,所以可以被设计器使用。
  6. BackgroundWorker使用了线程池,这意味着你永远都不能在一个BackgroundWorker线程上调用Abort方法
static BackgroundWorker _bw = new BackgroundWorker(); public static void MainThread() { _bw.DoWork += new DoWorkEventHandler(_bw_DoWork); _bw.RunWorkerAsync("Message to worker"); Console.ReadLine(); } static void _bw_DoWork(object sender, DoWorkEventArgs e) { Console.WriteLine(e.Argument); //做一些耗时的操作。 }
下面的例子实现了进度报告。
class ThreadBackgroundWorker { static BackgroundWorker _bw; public static void MainThread() { _bw = new BackgroundWorker { WorkerReportsProgress = true, //允许报告进度 WorkerSupportsCancellation = true //允许取消 }; _bw.DoWork += new DoWorkEventHandler(_bw_DoWork); _bw.ProgressChanged += new ProgressChangedEventHandler(_bw_ProgressChanged); _bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_bw_RunWorkerCompleted); _bw.RunWorkerAsync("Hello to worker"); Console.WriteLine("Press Enter in the next 5 seconds to cancel."); Console.ReadLine(); if (_bw.IsBusy) _bw.CancelAsync(); Console.ReadLine(); } static void _bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled) //是否取消 Console.WriteLine("You canceled!"); else if (e.Error != null) //是否有异常 Console.WriteLine("Worker exception:" + e.Error.ToString()); else Console.WriteLine("Complete:" + e.Result); } static void _bw_ProgressChanged(object sender, ProgressChangedEventArgs e) { //输出进度报告 Console.WriteLine("Reached " + e.ProgressPercentage + "%"); } static void _bw_DoWork(object sender, DoWorkEventArgs e) { for (int i = 0; i <= 100; i += 20) { if (_bw.CancellationPending) { e.Cancel = true; return; } _bw.ReportProgress(i); //报告进度 Thread.Sleep(1000); } e.Result = 123; } }

参考资料:
http://www.albahari.com/threading/
CLR Via C# 3.0
Tags: 

延伸阅读

最新评论

发表评论