Изменения в пользовательском интерфейсе обновляются в потоке, и неизвестно, какой поток выполняет обновление - PullRequest
0 голосов
/ 01 декабря 2011

В данной форме код ссылается на статический элемент в главной форме и обновляет его.

SomeRandomForm.cs

MainForm.Instance.UpdateSomeLabel("hello world");

Теперь не очевидно, находится ли этот код, который обновляет статический экземпляр основной формы метки (или чем-то еще), в потоке пользовательского интерфейса или нет.

Можно ли мне как-то обернуть этот вызов метода (UpdateSomeLabel), чтобы убедиться, что он обновляется только в потоке пользовательского интерфейса? Я хочу изменить метод UpdateSomeLabel, чтобы это обновление происходило только в потоке пользовательского интерфейса.

Просто пытаюсь очистить это приложение, чтобы сделать его безопасным.

Ответы [ 3 ]

3 голосов
/ 01 декабря 2011

Вы должны ознакомиться с методами Invoke (...) и BeginInvoke (...) и определить, какая семантика необходима для этого межпоточного обновления. Хотите запустить и забыть, а не ждать, пока насос сообщений вернется к вашему текстовому обновлению? Используйте BeginInvoke(...). Вы хотите, чтобы поток блокировался до обновления UI? Используйте Invoke(...).

Кроме того, имейте в виду, что если форма потенциально может быть удалена или еще не была показана, перед вызовом Invoke(...) или BeginInvoke(...) необходимо проверить не только флаг InvokeRequired в форме. Если есть вероятность этого случая, ваша логика должна выглядеть примерно так:

public void DoSomeUpdate(string text)
{
    if(this.IsDisposed || (!this.IsHandleCreated))
    {
        // error condition; run away!
        return;
    }

    if(this.InvokeRequired)
    {
        // BeginInvoke or Invoke here, based on the semantics you need
        return;
    }

    this.UpdateSomeLabel(text);
}

Прокомментируйте, пожалуйста, если что-то здесь неясно.

Обновление

Был некоторый комментарий относительно проверки того, была ли создана ручка управления или была ли она удалена.

Что касается первого случая (IsHandleCreated), это, безусловно, то, что вы можете предположить заранее во многих случаях; Однако не во всех случаях. Если вы не создаете динамически элементы управления, и ваши фоновые потоки запускаются через поток пользовательского интерфейса, вы, вероятно, в безопасности. Однако ...

Если есть какая-либо подсказка о том, что задание фонового потока может быть достаточно продолжительным, чтобы вы могли щелкнуть «X» в форме и закрыть ее (тем самым утилизировав ее), тогда вы получите приятное исключение. если вы не проверите IsDisposed перед звонком Invoke(...) или BeginInvoke(...). Это похоже на (в C) проверку на malloc(...), возвращающую что-то ненулевое. Это случается Как бы утомительно это ни было, автор приложения обязан заботиться об этих случаях потенциальной ошибки. Это добавляет осложнений; однако это важно, особенно если вы не хотите, чтобы ваше приложение зависало. Неправильный учет учетных записей IsDisposed и IsHandleCreated при вызове Invoke(...) или BeginInvoke(...) подвергает вас возможности сбоя приложения. Вы можете избежать этого сбоя с помощью нескольких дополнительных строк кода.

1 голос
/ 11 сентября 2012

FMM высказал свое мнение по поводу своего ответа. Я не принял во внимание возможность того, что дескриптор не был бы создан или элемент управления был удален. Вопрос, который был задан (в другом сообщении): «Каков самый простой способ?» Это расширение довольно просто, вы не согласны?

При этом я также дал ему только небольшой фрагмент расширения. Аналогичный «Invoke» также существует.

using System;
using System.Windows.Forms;

public static class Extensions
{
    /// <summary>
    /// Executes the Action asynchronously on the UI thread, does not block execution on the calling thread.
    /// </summary>
    /// <param name="this">The control responsible for performing the <param ref="code" /></param>
    /// <param name="code">The Action to be performed on the Control.</param>
    public static void UIThread(this Control @this, Action code)
    {
        // Check for error
        if (@this == null || !@this.IsHandleCreated || @this.IsDisposed)
        { return; }

        // Execute code
        if (@this.InvokeRequired)
        { @this.BeginInvoke(code); }
        else { code.Invoke(); }
    }

    /// <summary>
    /// Executes the Action on the UI thread, blocks execution on the calling thread until Action has been completed.
    /// </summary>
    /// <param name="this">The control responsible for performing the <param ref="code" /></param>
    /// <param name="code">The Action to be performed on the Control.</param>
    public static void UIThreadInvoke(this Control @this, Action code)
    {
        // Check for error
        if (@this == null || !@this.IsHandleCreated || @this.IsDisposed)
        { return; }

        // Execute code
        if (@this.InvokeRequired)
        { @this.Invoke(code); }
        else { code.Invoke(); }
    }
}

Он даже проверяет, чтобы убедиться, что элемент управления был создан в первую очередь, а не выбрасывает возможное исключение NullReferenceException, которое может быть возможным с расширениями.

1 голос
/ 01 декабря 2011

Самое элегантное средство, которое я когда-либо видел, чтобы убедиться, что метод WinForms работает в потоке пользовательского интерфейса, - это ответ от StyxRiver ( не принятый ответ, прокрутите вниз) в этом посте:

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...