如何停止 WPF 中的后台工作人员?
Posted
技术标签:
【中文标题】如何停止 WPF 中的后台工作人员?【英文标题】:How to stop background worker in WPF? 【发布时间】:2021-12-23 23:41:04 【问题描述】:我在我的 WPF 应用程序中使用 MVVM 模型。我有一个绑定到取消按钮的命令。我有一个启动按钮,可以启动一些后台工作人员。当我单击取消按钮时,我希望所有后台工作人员停止/退出。 当我点击取消按钮时,使用我当前的代码,后台工作人员不会停止并且“StartEngineeringOperation”完成。谁能帮我解决我在这里做错了什么?
当前代码:
对于 EngineeringViewModel.cs:
公共类 EngineeringViewModel
public EngineeringViewModel()
StartEngineering= new DelegateCommand(o =>
worker = new BackgroundWorker
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
;
worker.ProgressChanged += Worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
if (worker.IsBusy != true) worker.RunWorkerAsync();
worker.DoWork += (s, e) =>
StartEngineeringOperation();
if (worker.CancellationPending)
e.Cancel = true;
return;
;
,
k => true);
Cancel = new DelegateCommand(CancelEngineeringOperation);
private void StartEngineeringOperation()
startAlarmService();
startTrendQualityCheck();
private void CancelEngineeringOperation(object param)
worker.DoWork += (s, e) =>
if (worker.IsBusy)
worker.CancelAsync();
e.Cancel = true;
return;
;
我试过这个: 但似乎不起作用:
private void StartEngineeringOperation()
startAlarmService();
if (worker.CancellationPending)
e.Cancel = true;
return;
startTrendQualityCheck();
【问题讨论】:
您不能只是“取消”任意代码。您的StartEngineeringOperation
必须手动检查是否请求取消并(优雅地)停止它正在做的事情。
考虑使用async await Asynchronous programming 而不是后台工作人员。还有CancellationToken用于取消多线程操作。
Felix 已经指出 BackgroundWorkers
已过时,您应该使用 Task.Run
和 async
和 await
即使后台工作人员可能已经过时 - 使用带有取消令牌的任务或其他任何东西都不能解决一般问题。
相关:How to stop BackgroundWorker correctly
【参考方案1】:
正如您可能从 te cmets 中了解到的,您需要在您想要支持取消的操作中轮询 BackgroundWorker
的状态。然后采取措施优雅地取消正在进行的操作。
该示例显示了如何在单击按钮时取消后台线程。第一个示例使用旧的BackgroundWorker
,第二个示例使用现代且更简洁的任务库。
后台工作人员
private BackgroundWorker Worker get; set;
private void StartWorker()
this.Worker = new BackgroundWorker
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
;
this.Worker.DoWork += BackgroundWorker_DoWork;
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
BackgroundWorker worker = sender as BackgroundWorker;
DoCancellableWork();
// Stop BackgroundWorker from executing
if (worker.CancellationPending)
e.Cancel = true;
private void DoCancellableWork()
// Check for cancellation before executing the cancellable operation and allocating resources etc..
if (this.Worker.CancellationPending)
return;
// Periodically/regularly check for the cancellation flag
for (int i = 0; i <= 10000000000; i++)
if (this.Worker.CancellationPending)
// Cancel operation gracefully e.g., do some cleanup, free resources etc.
return;
// Do some work
// Alternatively use a command e.g., in a view model class
private void CancelBackgroundWorker_Click(object sender, EventArgs e)
if (this.Worker.WorkerSupportsCancellation)
this.Worker.CancelAsync();
任务库
该示例使用Progress<T> 从后台线程向 UI 线程报告进度。
private CancellationTokenSource CancellationTokenSource get; set;
private async Task StartWorker()
this.CancellationTokenSource = new CancellationTokenSource();
// Prepare callback to update UI from the background thread.
// The Progress<T> instance MUST be created on the UI thread
IProgress<int> progressReporter = new Progress<int>(progress => this.ProgressBar.Value = progress);
await Task.Run(
() => DoWork(progressReporter, this.CancellationTokenSource.Token),
this.CancellationTokenSource.Token);
this.CancellationTokenSource.Dispose();
private void DoWork(IProgress<int> progressReporter, CancellationToken cancellationToken)
DoCancellableWork(progressReporter, cancellationToken);
private void DoCancellableWork(IProgress<int> progressReporter, CancellationToken cancellationToken)
// Check for cancellation before executing the operation and allocating resources etc..
if (cancellationToken.IsCancellationRequested)
return;
// Periodically/regularly check for the cancellation flag
for (int i = 0; i <= 10000000000; i++)
if (cancellationToken.IsCancellationRequested)
// Cancel operation gracefully e.g., do some cleanup, free resources etc.
return;
// Do some work
// Report progress
progressReporter.Report(20);
// Alternatively use a command e.g., in a view model class
private void CancelBackgroundThread_Click(object sender, EventArgs e)
this.CancellationtokenSource?.Cancel();
【讨论】:
感谢@BionicCode 的回答。但我的 CancelableWork 不需要定期进行。所以使用 for 循环是行不通的。我需要在工作开始和完成之间的任何时间取消工作,具体取决于单击取消按钮。要做的工作不是循环的。如何在不使用循环的情况下轮询“IsCancellationRequested”? 这与循环无关。这就是为什么我在循环上方的代码注释中写了“定期/定期检查...”。 2) before 调用另一个长时间运行的操作(而长时间运行的操作会自己执行有用的检查,以便在长时间运行的操作开始后它可以自行取消) .这意味着您必须决定何时检查 CancellationPending。 3) 可能在持久化数据之前(写入文件、数据库等)。 4) 在创建结果或更新 UI 之前。所有这些考虑因素都适用于被调用的每个方法(调用树)。【参考方案2】:由于 OP 将正在完成的任务描述为“检查服务”,我假设完成的工作如下所示:
while(true)
// check service
// Post result back to UI thread
Thread.Sleep(...);
这不是编写此类检查的最佳方式。在大多数使用 Thread.Sleep 的情况下,计时器会是更好的选择:
var myTimer = new System.Timers.Timer(...);
myTimer .Elapsed += OnTimedEvent;
myTimer .AutoReset = true;
myTimer .Enabled = true;
...
private void OnTimedEvent(Object source, ElapsedEventArgs e)
// check service
// Post result back to UI thread
这使得停止/启动正在完成的任务的问题变得简单,只需更改计时器的启用标志即可。也可以使用计时器或同步上下文直接在 UI 线程上运行事件,如果“检查服务”只需要几毫秒,这可能是最好的解决方案。
【讨论】:
以上是关于如何停止 WPF 中的后台工作人员?的主要内容,如果未能解决你的问题,请参考以下文章
用户使用 GridSplitter 后 WPF 行高绑定停止工作
用于 RibbonTab 的 WPF IsSelected 绑定在 ctrl+tab 上停止工作