Определите, что блокирует поток пользовательского интерфейса

Я работаю над довольно большим приложением реального времени .NET WPF. Приложение работает отлично, как и ожидалось, за исключением одной БОЛЬШОЙ проблемы — обновление пользовательского интерфейса происходит медленно.

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

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

Есть ли способ определить, какой обработчик событий вызывает узкое место?


person c0D3l0g1c    schedule 21.09.2010    source источник


Ответы (4)


Я полностью поддерживаю предложение colithium об использовании профайлера.

Кроме того, если блокировка занимает больше секунды, вы можете нажать кнопку «Пауза» в Visual Studio. На панели инструментов есть раскрывающийся список, в котором вы можете выбрать «Основной поток». Затем он переходит к методу, который в данный момент блокирует пользовательский интерфейс.

person Heinzi    schedule 21.09.2010
comment
Спасибо Хайнце! Попробовал ваше предложение, и это сработало чудесно. Очень полезный и простой трюк для запоминания. Хотите верьте, хотите нет, но проблема была в журнале приложений. Использование приложения log4net для обновления пользовательского интерфейса журнала RichTextBox. - person c0D3l0g1c; 21.09.2010

  public class UIBlockDetector
{
    static Timer _timer;
    public UIBlockDetector(int  maxFreezeTimeInMilliseconds = 200)
    {
        var sw = new Stopwatch();

        new DispatcherTimer(TimeSpan.FromMilliseconds(10), DispatcherPriority.Send, (sender, args) =>
        {
            lock (sw)
            {
                sw.Restart();
            }

        }, Application.Current.Dispatcher);

        _timer = new Timer(state =>
        {
            lock (sw)
            {
                if (sw.ElapsedMilliseconds > maxFreezeTimeInMilliseconds)
                {
                    // Debugger.Break() or set breakpoint here;
                    // Goto Visual Studio --> Debug --> Windows --> Theads 
                    // and checkup where the MainThread is.
                }
            }

        }, null, TimeSpan.FromMilliseconds(0), TimeSpan.FromMilliseconds(10));

    }

}

Просто создайте этот класс в конструкторе MainWindow. Когда сработает точка останова, вы можете перейти в Visual Studio --> Отладка --> Windows --> Потоки и проверить, какая операция заблокировала ваш UI-поток!

person Andreas    schedule 28.01.2014
comment
Создайте класс в выбранном вами месте + установите точку останова внутри if (sw.ElapsedMilliseconds › maxFreezeTimeInMilliseconds). Этот класс является просто логикой сторожевого таймера для основного потока. - person Andreas; 19.01.2015
comment
Какой таймер вы используете здесь? - person frostymarvelous; 22.03.2016
comment
Вот это да. Это работает действительно очень здорово. Хороший там. Я буду держать его под рукой. - person frostymarvelous; 22.03.2016
comment
один из самых полезных фрагментов, которые я когда-либо хранил! спасибо @Андреас - person Ahmed Fwela; 28.09.2017
comment
@Andreas Андреас, 10 миллисекунд вызовут какие-либо узкие места в основном потоке от DispatcherTimer? Есть ли причина, по которой вы выбрали это значение? - person mattyb; 08.01.2019
comment
Это гениально и очень помогло мне. Спасибо! - person xxdefaultxx; 04.07.2021

У вас есть доступ к профилировщику кода? Это то, в чем они хороши. Я рекомендую получить его, если ответ отрицательный.

Кроме использования профилировщика. Вы можете выполнить профилирование «для бедняков», поместив операторы времени в начале и в конце блоков кода, которые вы подозреваете. Вы даже можете использовать точки останова и синхронизировать их с настенными часами. Проблема возникает, когда вы что-то нажимаете? Если это так, начните с этого. Это повторяющаяся проблема без взаимодействия с пользователем? Тогда начните с таймеров.

Что касается фактического решения проблемы... Если обработчик-нарушитель не делает что-то, что можно сделать более эффективным, рассмотрите возможность использования многопоточного подхода. В этом отношении новая библиотека задач для .NET 4.0 просто потрясающая.

person colithium    schedule 21.09.2010
comment
Привет, Колитий. Спасибо за ответ. Приложение является многопоточным — с использованием параллелизма в моих запросах и циклах Linq. Кроме того, вместо работы с объектами Thread я использую Task.Factory.StartNew для многопоточности. Приложение не содержит таймеров и фоновых рабочих процессов. У меня есть основная неблокирующая задача (поток), которая содержит только бизнес-логику, то есть вообще не взаимодействует с пользовательским интерфейсом. Логика этой задачи выполняет определенные действия и вызывает события, связанные с этими действиями. Я использую VS2010, в котором есть профилировщик. Не знаю, как им пользоваться и как оценивать результаты. - person c0D3l0g1c; 21.09.2010

В качестве приближения первого порядка я считаю полезным взломать отладчик (с кнопкой паузы в IDE) и посмотреть на стек. Сделайте это несколько раз, и вы увидите, есть ли закономерность. Вы всегда в одной и той же функции? Вы делаете что-то дорогое в ответ на событие? Получаете ли вы больше событий, чем ожидаете? Это низкотехнологично, но может быть очень эффективным.

person Community    schedule 21.09.2010