Как безопасно обновить пользовательский интерфейс WinForm из фонового потока?

У меня есть 2 winforms:

  • Form1
  • Form2

Form1 является основной формой. Форма 1 открывает форму 2. В форме2 Load обработчик событий, новый фоновый рабочий поток запущен. Когда рабочий поток завершает свою работу, он уведомляет поток пользовательского интерфейса об обновлении Form2.

Вопрос в том, что пользователь может закрыть Form2, пока рабочий поток еще работает. Таким образом, Form2 может исчезнуть к моменту окончания рабочего потока. Затем возникает некоторое исключение Null Reference, когда рабочий поток пытается обновить пользовательский интерфейс Form2.

Я планировал использовать флаг, чтобы указать существование Form2. Каждый раз при обновлении пользовательского интерфейса флаг проверяется, чтобы убедиться, что Form2 существует. Но эта схема проверки и действия не может справиться с расой. Потому что форма может быть закрыта после прохождения проверки, но до того, как будут предприняты действия по обновлению пользовательского интерфейса.

Так есть ли способ решить эту проблему?

Некоторый код формы 2:

private void StartComputeGraphWorker()
{// this runs on the UI thread.
    try
    {
        this.generationFinished = false;
        DisableAllControls(); //prevent user input while some background work is underway.
        StartShowProgressMarquee();
        ThreadStart d = new ThreadStart(WorkerStartWrapper);
        worker = new Thread(d);
        worker.IsBackground = true;
        worker.Start();
    }
    catch (Exception ex)
    {
        Logger.LogMessage(Logger.LogLevel.Error, ex.Message);
        EnableAllControls();
        StopShowProgressMarquee();
    }
}


private void NotifyUI(Boolean suceess)
{
    if (suceess)
    {
        // this is on the secondary illustration form. it may NOT exist by now.
        if (!this.formClosed)
        {//race conditions here
            this.Invoke(new MethodInvoker(ShowGraphDataInUIThread));
        }
        else//the form has been closed, we have no place to show the graph, just return.
        {
            return;
        }
    }
    else
    {
        // this logs to the main input form, it always exists.
        Logger.LogMessage(Logger.LogLevel.Warning, "Graph generation failed."); 
    }
}

private void WorkerStartWrapper()
{
    try
    {
        RenderGraphWorker();
        NotifyUI(true);
    }
    catch (Exception ex) // ThreadAbortException or Other Exceptions
    {
        Logger.LogMessage(Logger.LogLevel.Warning, ex.Message);
        NotifyUI(false);
    }
}

ДОБАВИТЬ 1

Я проверил ниже темы:

Как обновить графический интерфейс из другого потока в C#?

Это не совсем то же самое. Моя форма может исчезнуть. Это не только обновление управления несколькими потоками.

С подходом BackgroundWorker отмена подписки на событие RunWorkerCompleted в событии закрытия формы Form2 может решить мою проблему.

Но мне все еще интересно, возможно ли это с помощью класса Thread.

1 ответ

Сегодня я переосмысливаю подход отписки. Это выглядит хорошо. Но на самом деле, возможно, нет.

Есть условие гонки, что код может выполняться в обработчике завершенного события, пока я отписываюсь. Поэтому отказ от подписки не помешает ему манипулировать, возможно, несуществующей формой.

Я думаю, что я все еще должен придерживаться стандартной парадигмы BGW и решить эту проблему другим путем.

В моем сценарии у пользователя есть 2 способа отменить операцию BGW.

  • Нажмите на Cancel кнопка.
  • Закройте форму.

Мое текущее решение:

Если пользователь нажмет Cacncel кнопку, я покажу некоторые уведомления пользовательского интерфейса в Cancel обработчик нажатия кнопки перед вызовом bgw.CancelAsync(), Что-то вроде этого:

this.label1.Text = "Operation Cancelled";
bgw.CancelAsync()

В настоящее время пользовательский интерфейс гарантированно существует.

Если пользователь закроет форму, я просто позвоню bgw.CancelAsync() в обработчике событий закрытия формы. Тогда BGW.DoWork() будет опросить и найти этот сигнал и остановить выполнение. Мне не нужно уведомление пользователя интерфейса здесь, потому что это неявное намерение пользователя.

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

Таким образом, чтобы подвести итог, оставьте BGW, чтобы завершить свой жизненный цикл.

Другие вопросы по тегам