Могу ли я получить DoWorkEventArgs, когда BackgroundWorker завершит работу, прежде чем он будет передан в DoWork?
Я пытаюсь исправить проблему в существующем графическом интерфейсе, большая часть которой была решена с помощью кода, вставленного ниже из этого ответа -> Как ждать отмены BackgroundWorker?
private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);
public Form1()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
}
public void Cancel()
{
worker.CancelAsync();
_resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
// IS THERE ANY WAY TO TELL IF THE BACKGROUNDWORKER STOPPED DUE TO e.Cancel here???
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(!e.Cancel)
{
// do something
}
_resetEvent.Set(); // signal that worker is done
}
Мой вопрос добавлен в качестве комментария в конец функции Отмена. Есть ли способ узнать в этот момент, почему фоновый работник отключился?
1 ответ
Есть много вариантов. Вы, наверное, можете проверить CancellationPending
... Я не помню, если это сбрасывается, когда DoWork
обработчик возвращается - я сомневаюсь в этом, но если это произойдет, вы столкнетесь с гонкой между вашим ожиданием и возвращением обработчика. (Я бы просто протестировал его сам, но ваш вопрос не включает хороший пример кода Minimal, Complete и Verifiable, и я не потрудился создать его.)
Другой способ, вместо AutoResetEvent
использовать TaskCompletionSource<T>
, который поддерживает семантику завершения / отмены:
private BackgroundWorker worker = new BackgroundWorker();
private TaskCompletionSource<object> _tcs;
public Form1()
{
InitializeComponent();
worker.DoWork += worker_DoWork;
// Need to reinitialize when you actually start the worker...you
// didn't show that code, so I'm putting it here
_tcs = new TaskCompletionSource<object>();
}
public async Task Cancel()
{
worker.CancelAsync();
try
{
await _tcs.Task;
// you'll be here if the task completed normally
}
catch (TaskCancelledException)
{
// you'll be here if the task was cancelled
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
while(!e.CancellationPending)
{
// do something
}
if (e.CancellationPending)
{
_tcs.SetCanceled();
}
else
{
// if you actually want to return a value, you can set whatever
// value you want here. You can also use the correct type T for
// the TaskCompletionSource<T> declaration.
_tcs.SetResult(null);
}
}
Ты можешь позвонить Cancel()
как Cancel().Wait()
если хотите, но еще лучше, если вы можете использовать await
там, так что вы можете избежать блокировки потока.
Даже лучше, чем это, чтобы перейти от BackgroundWorker
в Task
а также CancellationTokenSource
, Тогда то, что вы ждете, может быть самой задачей, а не прокси для этой задачи.