Почему накладные расходы на многопоточный доступ к элементам управления пользовательского интерфейса WPF? - PullRequest
1 голос
/ 18 марта 2009

Я что-то упустил с WPF (и Windows Forms до этого) в связи с необходимостью писать много повторяющегося кода при разработке многопоточных приложений. Каждый элемент управления пользовательского интерфейса приложения требует дополнительных строк кода для получения и установки каждого свойства.

internal delegate void SetElementIsEnabledDelegate(UIElement element, bool isEnabled);
internal delegate void SetBrushesAndTextDelegate(Brush foregroundBrush, string message);
internal delegate void SetCheckBoxCheckedDelegate(CheckBox checkbox, bool checkedValue);
internal delegate void SetTextBoxTextDelegate(TextBox richTextBox, string text);
internal delegate void SetRichTextBoxTextDelegate(RichTextBox textBox, string text);

internal static void SetElementIsEnabled(UIElement element, bool isEnabled)
{
    if (element.Dispatcher.Thread != Thread.CurrentThread)
    {
        // Execute the same method, but this time on the GUI thread
       element.Dispatcher.Invoke(DispatcherPriority.Normal, new SetElementIsEnabledDelegate(SetElementIsEnabled), element, isEnabled);
       return;
   }

   element.IsEnabled = isEnabled;
}

Добавьте к этому еще 30 делегатов и соответствующие методы.

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

Ответы [ 3 ]

3 голосов
/ 18 марта 2009

Ну, вы, конечно, можете сделать это проще, чем это. Во-первых, вам не нужны все ваши собственные отдельные типы делегатов - вы можете использовать встроенные Func<...> и Action<...> делегаты, если вы используете .NET 3.5, а если нет, вы можете объявить их самостоятельно а затем использовать их в общем.

Другая вещь, которую вы могли бы рассмотреть, - это написание вспомогательного метода (как метода расширения, если вы используете C # 3), а затем использовать анонимные методы (или лямбда-выражения):

internal static void SetElementIsEnabled(UIElement element, bool isEnabled)
{
    InvokeIfNecessary(element, delegate
    {
        element.IsEnabled = isEnabled;
    });
}

Вспомогательный метод будет:

public static void InvokeIfNecessary(UIElement element, MethodInvoker action)
{
    if (element.Dispatcher.Thread != Thread.CurrentThread)
    {
        element.Dispatcher.Invoke(DispatcherPriority.Normal, action);
    }
    else
    {
        action();
    }
}

(Вы, вероятно, также должны включать BeginInvoke эквивалент. Я обычно предпочитаю BeginInvoke, если мне не нужно свидание.)

2 голосов
/ 18 марта 2009

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

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

Кроме того, ваш код не особенно эффективен. лучший подход:

if ( InvokeRequired ) {
    Invoke(new MethodInvoker(this.Function));
} else {
    // do code
}

РЕДАКТИРОВАТЬ : Полагаю, мой код больше для Windows Forms, а ваш - для WPF, но цель та же. Windows требует, чтобы все обновления элемента управления происходили в потоке, который создал элемент управления, из-за проблем с локальным хранилищем потока. И это не то, что должно быть там для всех целей.

0 голосов
/ 25 марта 2009

Не забудьте лямбды:

int myParam = 5;

Dispatcher.BeginInvoke((Action)(() => SomeMethod(myParam)));

В диспетчере также есть скрытый метод intellisense:

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