C# 异步编程—BackgroundWorker类(暂停/继续)
BackgroundWorker类介绍:有时候可能需要另建一个线程,在后台持续运行以完成某些工作,并不时地与主线程进行通信,这时就可以用到BackgroundWorker类来实现;
属性:WorkerReportsProgress和WorkerSupportsCancellation这两个属性用于设置后台任务是否可以把它的进度汇报给主线程以及是否支持从主线程取消;IsBusy属性来检查后台任务是否正在运行;
事件:DoWork,ProgressChanged和RunWorkerCompleted用来发送不同的程序事件和状态。你需要为自己的程序写这些事件的事件处理方法来执行适合程序的行为;在后台线程开始的时候触发DoWork,在后台任务汇报状态的时候触发ProgressChanged事件,后台工作线程退出的时候触发RunWorkerCompleted事件;
方法:三个方法RunWorkrAsync,CancelAsync和ReportProcess用于初始化行为或改变状态。
调用RunWorkrAsync方法获取后台线程并且执行DoWork事件处理程序;
调用CancelAsync方法把CancellationPending属性设置为true。DoWork事件处理程序需要检查这个属性来决定是否应该停止处理;
DoWork事件处理程序(在后台线程)在希望向主线程汇报进度的时候,调用Report-Process方法;
下面是一个WPF程序,点击Process按钮将开启后台线程,每半秒向主线程报告一次,并使进度条增长10%:
xaml代码:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ProgressBar Name="progressBar" Height="20" Width="200" Margin="10"></ProgressBar>
<Button Name="btnProcess" Width="200" Content="Process" Margin="5" Click="btnProcess_Click"></Button>
<Button Name="btnCancel" Width="200" Content="Cancel" Margin="5" Click="btnCancel_Click"></Button>
</StackPanel>
</Window>
主程序代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
BackgroundWorker bgWorker = new BackgroundWorker();
public MainWindow()
{
InitializeComponent();
//设置BackgroundWorker属性
//bool类型,指示BackgroundWorker是否可以报告进度更新。当该属性值为True是,将可以成功调用ReportProgress方法,否则将引发InvalidOperationException异常。
bgWorker.WorkerReportsProgress = true;
//bool类型,指示BackgroundWorker是否支持异步取消操作。当该属性值为True是,将可以成功调用CancelAsync方法,否则将引发InvalidOperationException异常。
bgWorker.WorkerSupportsCancellation = true;
//连接BackgroundWorker对象的处理程序
//DoWork:用于承载异步操作。当调用BackgroundWorker.RunWorkerAsync()时触发。
bgWorker.DoWork += DoWork_Handler;
//ProgressChanged:当调用BackgroundWorker.ReportProgress(int percentProgress)方式时触发该事件。
bgWorker.ProgressChanged += ProgressChanged_Handler;
//RunWorkerCompleted:异步操作完成或取消时执行的操作,当调用DoWork事件执行完成时触发。
bgWorker.RunWorkerCompleted += RunWorkerCompleted_Handler;
}
private void DoWork_Handler(object sender,DoWorkEventArgs args)
{
BackgroundWorker worker = sender as BackgroundWorker;
for(int i =1;i<=10;i++)
{
//CancellationPending:bool类型,指示应用程序是否已请求取消后台操作。此属性通常放在用户执行的异步操作内部,用来判断用户是否取消执行异步操作。当执行BackgroundWorker.CancelAsync()方法时,该属性值将变为True。
if(worker.CancellationPending)
{
args.Cancel = true;
break;
}
else
{
worker.ReportProgress( i * 10 );//报告操作进度。调用该方法后,将触发BackgroundWorker. ProgressChanged事件。另外,该方法包含了一个int类型的参数percentProgress,用来表示当前异步操作所执行的进度百分比。
Thread.Sleep(500);
}
}
}
private void ProgressChanged_Handler(object sender,ProgressChangedEventArgs args)
{
progressBar.Value = args.ProgressPercentage;
}
private void RunWorkerCompleted_Handler(object sender, RunWorkerCompletedEventArgs args)
{
progressBar.Value = 0;
if (args.Cancelled)
MessageBox.Show("Process was cancelled.", "Process Cancelled");
else
MessageBox.Show("Process completed normally.","Process Completed");
}
private void btnProcess_Click(object sender, RoutedEventArgs e)
{
//IsBusy:bool类型,指示BackgroundWorker是否正在执行一个异步操作。此属性通常放在BackgroundWorker.RunWorkerAsync()方法之前,避免多次调用RunWorkerAsync()方法引发异常。当执行BackgroundWorker.RunWorkerAsync()方法是,该属性值将变为True。
if (!bgWorker.IsBusy)
bgWorker.RunWorkerAsync();//调用该方法后,将触发BackgroundWorker.DoWork事件,并以异步的方式执行DoWork事件中的代码。
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
bgWorker.CancelAsync();//请求取消当前正在执行的异步操作。调用该方法将使BackgroundWorker.CancellationPending属性设置为True。
}
}
}
执行效果:
推荐一篇相关的博客:https://www.cnblogs.com/zhaoshujie/p/9634136.html
暂停与继续
BackgroundWorker bw;
private ManualResetEvent manualReset = new ManualResetEvent(true);
private void button3_Click(object sender, EventArgs e)
{
using ( bw = new BackgroundWorker())
{
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.ProgressChanged += bw_ProgressChanged;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.DoWork += bw_DoWork;
//允许用户指定显示数据的范围呢!所以需要把100作为参数传递给计算过程
bw.RunWorkerAsync(100);
}
}
//这时返回了主线程,所以可以直接使用UI控件了
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//修改进度条的显示。
//this.progressBarSum.Value = e.ProgressPercentage;
//如果有更多的信息需要传递,可以使用 e.UserState 传递一个自定义的类型。
//这是一个 object 类型的对象,您可以通过它传递任何类型。
//我们仅把当前 sum 的值通过 e.UserState 传回,并通过显示在窗口上。
string message = e.UserState.ToString();
label1.Text = message;
}
//e.Argument=bw.RunWorkerAsync("Hello World")的参数
void bw_DoWork(object sender, DoWorkEventArgs e)
{
System.Diagnostics.Debug.WriteLine("bw_DoWork");
BackgroundWorker bgWorker = sender as BackgroundWorker;
//这里的操作是在另一个线程上完成的,不应该操作UI
//在这里执行耗时的运算。
int endNumber = 0;
if (e.Argument != null)
{
endNumber = (int)e.Argument;
}
for (int i = 0; i <= endNumber; i++)
{
manualReset.WaitOne();
//如果ManualResetEvent的初始化为终止状态(true),那么该方法将一直工作,
//直到收到Reset信号。然后,直到收到Set信号,就继续工作。
bgWorker.ReportProgress(i, "current num:" + i.ToString());
Thread.Sleep(500); //为了方便演示
if (bgWorker.CancellationPending)
{
e.Cancel = true;
System.Diagnostics.Debug.WriteLine("CancellationPending");
break;
}
}
}
//停止
private void button4_Click(object sender, EventArgs e)
{
bw.CancelAsync();
}
//暂停/继续
private void button5_Click(object sender, EventArgs e)
{
if (btnPause.Text == "暂停")
{
manualReset.Reset();//暂停当前线程的工作,发信号给waitOne方法,阻塞
btnPause.Text = "继续";
}
else
{
manualReset.Set();//继续某个线程的工作
btnPause.Text = "暂停";
}
}
ManualResetEvent 允许线程通过发信号互相通信。通常,此通信涉及一个线程在其他线程进行之前必须完成的任务。当一个线程开始一个活动(此活动必须完成后,其他线程才能开始)时,它调用 Reset 以将 ManualResetEvent 置于非终止状态,此线程可被视为控制 ManualResetEvent。调用 ManualResetEvent 上的 WaitOne 的线程将阻止,并等待信号。当控制线程完成活动时,它调用 Set 以发出等待线程可以继续进行的信号。并释放所有等待线程。一旦它被终止,ManualResetEvent 将保持终止状态(即对 WaitOne 的调用的线程将立即返回,并不阻塞),直到它被手动重置。可以通过将布尔值传递给构造函数来控制 ManualResetEvent 的初始状态,如果初始状态处于终止状态,为 true;否则为 false。
链接:https://www.cnblogs.com/zitjubiz/p/11533555.html