С# - фоновый рабочий 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 или написать собственный код.