С# - фоновый рабочий CancelAsync() не работает?
Я хочу прервать процесс, но не могу этого сделать, я использую Background worker с моими функциями обработки.
public void Init()
{
bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
if (bw.CancellationPending == true)
{
e.Cancel = true;
}
else
{
e.Result = abd();
}
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled)
{
lbltext.content="Canceled";
}
else
{
lbltext.content="Completed";
}
}
private void btncan_Click(object sender, RoutedEventArgs e)
{
bw.CancelAsync();
}
private void btnstart_Click(object sender, RoutedEventArgs e)
{
bw.RunWorkerAsync();
}
Я не могу прервать процесс, используя этот код.
Функция abd()
выполняет обработку и возвращает результат.
Пожалуйста, предоставьте мне какое-либо решение.
Спасибо.
Ответы
Ответ 1
Когда вы вызываете bw.CancelAsync()
, вы просто устанавливаете флаг CancellationPending
на true
. По умолчанию это не отменяет. Вам необходимо обрабатывать ожидающие отмены вручную. Но вы не можете сделать это с помощью своего кода, потому что когда вы нажимаете кнопку, возможны три варианта:
- Долгосрочный метод
abd()
завершил работу, и отменить нечего.
-
abd()
запустил его работу, а фоновый рабочий заблокирован - он ждет результатов abd()
, затем он продолжает выполнение - то есть выходит из блока if-else
и вызывает событие RunWorkerCompleted
.
- Почти невозможный вариант - вы будете быстрыми, как свет, и вы нажмете кнопку до ввода
if-else
. Чем CancellationPending
будет истинно, а abd()
не запустится.
Если вы хотите использовать отмену, выполните свою длительную задачу в цикле и проверьте, не отменено ли отмена на каждом шаге:
void bw_DoWork(object sender, DoWorkEventArgs e)
{
List<Foo> results = new List<Foo>();
// any loop here - foreach, while
for(int i = 0; i < steps_count; i++)
{
// check status on each step
if (bw.CancellationPending == true)
{
e.Cancel = true;
return; // abort work, if it cancelled
}
results.Add(abd()); // add part of results
}
e.Result = results; // return all results
}
Ответ 2
Вероятно, DoWork
, возможно, закончил свою работу перед вызовом CancelAsync
и, как упоминалось в документации e.Cancelled
, может быть ложным..
Документы говорят об этом
Помните, что ваш код в обработчике событий DoWork может завершить работа в качестве запроса на отмену, и ваш цикл опроса может пропустить CancellationPending, установленное в true. В этом случае Отмененный флаг System.ComponentModel.RunWorkerCompletedEventArgs в ваш обработчик события RunWorkerCompleted не будет установлен в true, даже хотя был сделан запрос на отмену. Эта ситуация называется расы и является общей проблемой в многопоточном программировании.
Ответ 3
Как насчет следующего?
While(!bw.CancellationPending)
{
//do some work!
}
e.Cancel = true;
Ответ 4
Похоже, функция abd()
отнимает слишком много времени на обработку, и вы хотите прервать ее между ними.
private Thread _backgroundWorkerThread;
public void AbortBackgroundWorker()
{
if(_backgroundWorkerThread != null)
_backgroundWorkerThread.Abort();
}
void DoWork(object sender, DoWorkEventArgs e)
{
try
{
_backgroundWorkerThread = Thread.CurrentThread;
// call abd...
}
catch(ThreadAbortException)
{
// Do your clean up here.
}
}
Поместите AbortBackgroundWorker()
в событие btncan_Click
. Но это идет с ценой. Теперь событие RunWorkerCompleted не будет работать. Итак, вам придется обработать его в блоке catch или написать собственный код.