C# 多线程 03-使用线程池 07-使用 BackgroundWorker 组件
🏷️ 《C# 多线程》
使用 BackgroundWorker 组件
csharp
/// <summary>
/// 使用 BackgroundWorker 组件
/// 借助于该对象,可以将异步代码组织为一系列事件及事件处理器
/// </summary>
/// <param name="args"></param>
static void Main(string[] args)
{
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true; // 是否可以报告进度更新
bw.WorkerSupportsCancellation = true; // 是否支持异步取消操作
bw.DoWork += Worker_DoWork;
bw.ProgressChanged += Worker_ProgressChanged;
bw.RunWorkerCompleted += Worker_Completed;
bw.RunWorkerAsync();
Console.WriteLine("Press C to cancel work");
do
{
if (Console.ReadKey(true).KeyChar == 'C')
{
bw.CancelAsync();
}
} while (bw.IsBusy);
}
static void Worker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine($"DoWork 线程池的线程 ID:{Thread.CurrentThread.ManagedThreadId}");
var bw = (BackgroundWorker)sender;
for (int i = 1; i <= 100; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
if (i%10 == 0)
{
// 触发 BackgroundWorker 的 ProgressChanged 事件
bw.ReportProgress(i);
}
Thread.Sleep(TimeSpan.FromSeconds(0.1));
}
e.Result = 42;
}
static void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Console.WriteLine($"已完成 {e.ProgressPercentage}%. Progress 线程池 Id:{Thread.CurrentThread.ManagedThreadId}");
}
static void Worker_Completed(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine($"Completed 线程池 Id:{Thread.CurrentThread.ManagedThreadId}");
if (e.Error != null)
{
Console.WriteLine($"发生异常:{e.Error.Message}");
}
else if (e.Cancelled)
{
Console.WriteLine($"操作已被取消.");
} else
{
Console.WriteLine($"结果为:{e.Result}.");
}
}
打印结果
Press C to cancel work
DoWork 线程池的线程ID:3
已完成 10%. Progress线程池Id:4
已完成 20%. Progress线程池Id:5
已完成 30%. Progress线程池Id:4
已完成 40%. Progress线程池Id:5
已完成 50%. Progress线程池Id:4
已完成 60%. Progress线程池Id:5
已完成 70%. Progress线程池Id:4
已完成 80%. Progress线程池Id:5
已完成 90%. Progress线程池Id:4
已完成 100%. Progress线程池Id:5
Completed线程池Id:4
结果为:42.
打印 Progress 的过程中,按下大写的 C 可以取消异步操作。
总结
这个例子中最有意思的部分在于没有使用线程池和委托,而是使用了事件。
事件表示了一些通知的源或当通知到达时会有所响应的一系列订阅者。
在例子中订阅了三个事件,当这些事件发生时,将调用相应的事件处理器。
bw.DoWork += Worker_DoWork;
bw.ProgressChanged += Worker_ProgressChanged;
bw.RunWorkerCompleted += Worker_Completed;
当事件通知了其订阅者时,具有特殊的定义签名的方法将被调用。
因此,除了将异步 API 组织为 Begin/End 方法对,还可以启动一个异步操作然后订阅给不同的事件。
这些事件在该操作执行时会被触发。
这种方式被称为基于事件的异步模式(Event-based Asynchronous Pattern,简称 EAP)。
这是历史上第二种用来构造异步程序的方式。