Прежде всего, BGW устарел, полностью заменен на async/await
, Задачи и Progress<T>
. Задачи позволяют составлять, продолжения и отмены, что довольно сложно с BGW. Я подозреваю, что событие bwMBExitEvent
используется для реализации продолжения после завершения BGW.
В статье Асинхронизация в 4.5: Включение хода выполнения и отмены в API-интерфейсах Async объясняется, как работают отмены и отчеты о ходе выполнения в .NET 4.5 и более поздних версиях (т. Е. Во всех поддерживаемых версиях).
Тем не менее, BGW не имеет проблем с отменой. Я приостанавливаю событие, loop
переменные и другой невидимый код в конечном итоге вызывают условия гонки.
Использование 2, 4 или 10 отменяемых задач вместо BGW, хотя это легко.
- Несколько задач можно легко запустить с помощью Task.Run .
- Можно ожидать завершения нескольких задач без блокировки с помощью Task.WhenAll .
- Отмена может сигнализироваться потокам, задачам при асинхронных операциях с помощью CancellationTokenSource
Легко запустить несколько задач:
private void StartTasks()
{
_cts=new CancellationTokenSource();
//Start each method passing a CancellationToken
_tasks=new[]{
Task.Run(()=>WorkerMethod1(_cts.Token)),
Task.Run(()=>WorkerMethod2(_cts.Token)),
...
};
//Enable the Cancel button
Cancel.Enabled=true;
}
Этот код создает N задач и сохраняет их в массиве. Он также создает новый CancellationTokenSource, который можно использовать для сигнализации об отмене всех задач или потоков, отслеживающих его токены
Чтобы отменить задачи с помощью кнопки, вызовите CancellationTokenSource.Cancel () и дождитесь завершения всех задач:
private async void Cancel_Clicked(object sender,EventArgs args)
{
if (_cts!=null)
{
lblStatus.Text = "Cancelling";
//Signal a cancellation
_cts.Cancel();
//Asynchronously wait for all tasks to finish
await Task.WhenAll(_tasks);
_cts=null;
lblStatus.Text = "Cancelled";
}
//Disable the button
Cancel.Enabled=false;
}
При использовании async/await
обработчик не блокируется в ожидании завершения задач. Ему не нужны Invoke
или BeginInvoke
, так как выполнение возобновляется в потоке пользовательского интерфейса после await
.
Все рабочие методы должны проверять CancellationToken.IsCancellationRequested флаг:
private void WorkerMethod1(CancellationToken token)
{
//If cancellation isn't requested
while(!token.IsCancellationRequested)
{
//Loop one more time
}
}
Собираем все вместе:
//Hold active tasks
Task[] _tasks;
private void WorkerMethod1(CancellationToken token)
{
//If cancellation isn't requested
while(!token.IsCancellationRequested)
{
//Loop one more time
}
}
CancellationTokenSource _cts;
private void OnLoad(...)
{
//Fire the tasks
StartTasks();
}
private void StartTasks()
{
_cts=new CancellationTokenSource();
//Start each method passing a CancellationToken
_tasks=new[]{
Task.Run(()=>WorkerMethod1(_cts.Token)),
Task.Run(()=>WorkerMethod2(_cts.Token)),
...
};
//Enable the Cancel button
Cancel.Enabled=true;
}
private async void Cancel_Clicked(object sender,EventArgs args)
{
if (_cts!=null)
{
//Signal a cancellation
_cts.Cancel();
//Asynchronously wait for all tasks to finish
await Task.WhenAll(_tasks);
_cts=null;
}
//Disable the button
Cancel.Enabled=false;
}