Event-based asynchronous(EAP)在多线程的环境中提供了一个简单的处理方式。
它有以下几个特性:
- 支持取消。
- 可以安全的更新WPF或windows Forms 控件。
- 在completion event中可以查询异常信息。
-
“在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序。
-
同时执行多个操作,每个操作完成时都会接到通知。
-
等待资源变得可用,但不会停止(“挂起”)您的应用程序。
-
使用熟悉的事件和委托模型与挂起的异步操作通信。
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下面的一个管理工作线程的帮助类,提供了下面几个特性
- 支持取消。
- 可以安全的更新WPF或windows Forms 控件。
- 在completion event中可以查询异常信息。
- 可以报告进度。
- 因为实现了IComponent接口,所以可以被设计器使用。
- 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