Skip to content

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)。

这是历史上第二种用来构造异步程序的方式。