Отмена BackgroundWorker
Я использую BackgroundWorker в своем приложении winforms для выполнения длительной задачи, которая выполняется в другом классе (выполнение операций с базой данных). Поскольку вся работа выполняется в другом классе, отмена не так проста. Я использую событие в другом классе (GenerateStats
) для обновления хода выполнения по мере завершения фоновой операции. Я хочу сделать то же самое с отменой операции. Я не могу просто вызвать cancellationPending
в функции DoWork, потому что метод никогда не увидит его до тех пор, пока он заканчивает, и это разрушает цель. Я хочу функциональность отмены без передачи BackgroundWorker в generateForSubject()
. Существует ли вообще, что это может поддерживать отмену из метода generateForSubject()
в классе GenerateStats
. Это экземпляр класса, в котором выполняется операция:
GenerateStats genStats = new GenerateStats();
Это моя функция DoWork, которая вызывает метод ReportProgress
всякий раз, когда вызывается событие в другом классе. Он также вызывает метод из другого класса generateForSubject()
, который выполняет оперативный.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
genStats.ProgressChanged += (s, pe) => worker.ReportProgress(pe.ProgressPercentage, pe.UserState);
genStats.generateForSubject();
}
Это обработчик событий нажатия кнопки, который должен инициировать отмену и запустить CancelAsync()
private void btnStop_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy)
{
backgroundWorker.CancelAsync();
}
}
Это мой отдельный класс, который выполняет операцию, а также создает обработчик событий ProgressChanged, чтобы мой класс мог обновить форму с информацией о ходе выполнения. Было бы здорово, если бы отмена могла действовать аналогичным образом.
public event ProgressChangedEventHandler ProgressChanged;
protected virtual void OnProgressChanged(int progress, string message)
{
if (ProgressChanged != null)
{
ProgressChanged(this, new ProgressChangedEventArgs(progress, message));
}
}
public void generateForSubject()
{
//Perform db operation not important, but it takes time
OnProgressChanged(33, "Finished 1st set of stats");
//I hope to check for cancellation here
//Perform db operation not important, but it takes time
OnProgressChanged(66, "Finished 2nd set of stats");
//I hope to check for cancellation here
//Perform db operation not important, but it takes time
OnProgressChanged(99, "Finished 3rd set of stats");
//I hope to check for cancellation here
}
Просто чтобы прояснить, есть ли какая-то неопределенность относительно того, о чем я спрашиваю, есть ли какой-либо способ для меня поддержать отмена моего backgroundWorker в другом классе без передачи backgroundWorker методу. Если нет абсолютно никакого способа, и я должен, то я передам backgroundWorker2 ответа:
Было бы полезно, если бы вы могли более конкретно рассказать о своем нежелании передавать экземпляр
Тем не менее, вы можете применить ту же философию, что и для событияBackgroundWorker
. Знание того, почему это требование к дизайну, может помочь лучше ответить на ваш вопрос.ProgressChanged
, и делегировать проверку отмены. Например:class GenerateStats { public event EventHandler<CancelEventArgs> CheckCancel; private bool OnCheckCancel() { EventHandler<CancelEventArgs> handler = CheckCancel; if (handler != null) { CancelEventArgs e = new CancelEventArgs(); handler(this, e); return e.Cancel; } return false; } public void generateForSubject() { //Perform db operation not important, but it takes time OnProgressChanged(33, "Finished 1st set of stats"); if (OnCheckCancel()) { // Or other cancellation logic here return; } //Perform db operation not important, but it takes time OnProgressChanged(66, "Finished 2nd set of stats"); if (OnCheckCancel()) { // Or other cancellation logic here return; } //Perform db operation not important, but it takes time OnProgressChanged(99, "Finished 3rd set of stats"); if (OnCheckCancel()) { // Or other cancellation logic here return; } } } private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker worker = sender as BackgroundWorker; genStats.ProgressChanged += (s, pe) => worker.ReportProgress(pe.ProgressPercentage, pe.UserState); genStats.CheckCancel += (sender1, e1) => e1.Cancel = worker.CancellationPending; genStats.generateForSubject(); }
Это позволяет классу
GenerateStats
проверять ожидание отмены, не имея прямого доступа к экземпляруBackgroundWorker
, как и событиеProgressChanged
. позволяет ему сообщать о прогрессе черезBackgroundWorker
без прямого доступа кBackgroundWorker
.
Ваш метод должен периодически проверять свойство CancellationPending, чтобы увидеть, был ли сделан запрос на отмену. А затем принять решение об отмене операции. Смотрите MSDN link для этого.
Из MSDN:
Имейте в виду, что ваш код в обработчике событий DoWork может завершить свою работу. работа как запрос на отмену производится, и ваш цикл опроса может пропустить отмену, если установить значение true. В этом случае Отмененный флаг Система.ComponentModel.RunWorkerCompletedEventArgs в обработчик событий RunWorkerCompleted не будет установлен в значение true, даже если хотя запрос на отмену был сделан. Эта ситуация называется состояние гонки и является общей проблемой в многопоточном программировании. Дополнительные сведения о проблемах многопоточного проектирования см. В разделе Управление Пронизывание Лучших Практик.
Редактировать:
Если вы не хотите передавать backgroundworker в метод generateForSubject. Установить свойство в классе при получении CancelAsync. И перед каждой операцией проверяйте значение этого свойства из метода generateForSubject.