Как закрыть поток BackgroundWorker, когда приложение деактивировано? - PullRequest
5 голосов
/ 01 октября 2011

Я создаю поток с BackgroundWorker, и в цикле я проверяю каждый раз, является ли CancellationPending истинным или нет, например:

   public MainPage()
    {
        InitializeComponent();

        bw = new BackgroundWorker();
        bw.WorkerReportsProgress = true;
        bw.WorkerSupportsCancellation = true;
        bw.DoWork += new DoWorkEventHandler(bw_DoWork);
        bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
        bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
    }

    private void ButtonStart_Click(object sender, RoutedEventArgs e)
    {
        if (bw.IsBusy != true)
        {
            bw.RunWorkerAsync();
        }
    }

    private void ButtonCancel_Click(object sender, RoutedEventArgs e)
    {
        if (bw.WorkerSupportsCancellation)
        {
            bw.CancelAsync();
        }
    }

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker worker = sender as BackgroundWorker;

        for (int i = 1; i <= 100; i++)
        {
            Debug.WriteLine("The tread is working");
            if (worker.CancellationPending)
            {
                e.Cancel = true;
                bw.CancelAsync();
                break;
            }
            else
            {

                System.Threading.Thread.Sleep(500);
                worker.ReportProgress(i);
            }
        }
    }

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Cancelled)
        {
            tbProgress.Text = "Canceled";
        }
        else if (e.Error != null)
        {
            tbProgress.Text = "Error: " + e.Error.Message;
        }
        else
        {
            tbProgress.Text = "Done";
        } 
    }

    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        tbProgress.Text = e.ProgressPercentage.ToString() + "%";
    }

Когда приложение деактивировано, поток не был закрыт, он прерывается и возникает исключение. Как закрыть темы с BackgroundWorker, когда приложение деактивировано?

Ответы [ 4 ]

10 голосов
/ 10 ноября 2011

Когда приложение деактивировано, каждый поток, кроме основного потока пользовательского интерфейса, генерирует исключение ThreadAbortException, как только оно становится активным.Похоже, что это «намеренно» как способ заставить приложения быстро прекратить то, что они делают.Потоки могут перехватить исключение ThreadAbortException и обернуть то, что они делают, но имейте в виду, что исключение ThreadAbortException будет автоматически возбуждено снова в конце блока catch.Любой код в блоке finally также будет выполнен.

Для вашей конкретной проблемы нет причин пытаться отменить BackgroundWorker, когда приложение деактивировано, поскольку возникнет исключение ThreadAbortException, которое эффективно остановит фонового работника.Если вы хотите что-то очистить, когда это происходит, вы ловите исключение ThreadAbortException в bw_DoWork, делаете то, что вам нужно сделать, и затем позволяете ему умереть.

Чтобы запустить его снова после активации, вам нужнонеобходимо перезапустить фоновый рабочий, как вы это делали при первом запуске приложения.

5 голосов
/ 02 октября 2011

Вы установили BackgroundWorker для отмены?

 var bg= new BackgroundWorker();
 bg.WorkerSupportsCancellation = true;

Из документации:

Установите для свойства WorkerSupportsCancellation значение true, если вы хотите, чтобы BackgroundWorker поддерживал отмену.Когда это свойство имеет значение true, вы можете вызывать метод CancelAsync для прерывания фоновой операции.

Также ваш код кажется неправильным, вы должны вызывать CancelAsync() вне кода вашего потока,это установит флаг CancellationPending, который вы можете использовать для выхода из цикла.

Хотя я не уверен на 100%, поскольку не знаю, откуда берется переменная bw.`

2 голосов
/ 02 октября 2011

Вам следует установить флажок CancellationPending в рабочем методе и корректно выйти, если этот флаг установлен.
Каково условие отмены задания?Отмена Нажмите кнопку?Затем вы должны поместить туда вызов CancelAsync (...).Он установит флаг CancellationPending для вас, который вы уже проверяете в своем коде, избегая необходимости чего-то вроде объекта ResetEvent.

1 голос
/ 12 февраля 2013

Я хочу добавить один уточняющий момент к ответам выше, основываясь на моих собственных наблюдениях. Фоновые потоки переживают деактивацию / активацию при сохранении экземпляра .

У меня был код, который выглядел так:

private void Application_Activated(object sender, ActivatedEventArgs e)
{
    if (e.IsApplicationInstancePreserved)
    {
        Log.write("Activating, instance preserved");
        // restart long-running network requests
        startBackground();
    }
    else // start over again
    {
        AppSettings.init();
        Log.write("Activating, rehydrating" );
        startBackground();
    }
}

Я также зарегистрировал хеш-код BackgroundWorker объект, когда он выполняется.Я увидел, что каждый раз, когда я получал сообщение «Активация, экземпляр сохранен», я получал дополнительный BackgroundWorker запуск.

Исходя из этого, представляется более точным сказать, что потоки прерываются при захоронении,не на дезактивации?

...