Comments 29
Интересный вариант. Теперь хорошо бы придумать автоматический способ обнаруживать такой блокирующий код, чтобы можно было его зарефакторить.
0
Можно попробовать Debugger.Launch, но вопрос что будет в стеке что делать дальше.
0
Наверное, задачу останова будет попроще решить.
+1
Ну что вы. Я же не прошу точно указать причину ошибки. Достаточно было бы обнаружить факт блокировки и получить callstack этого потока, чтобы отправить его в качестве багрепорта дальше уже в ручном режиме разобраться. Без этого приведенный в статье способ выглядит как заглушка — «мы знаем что проблема может быть, но локализовать и исправить не можем, так что пока хоть так».
+2
Отличный пост, приму на заметку.
Небольшой комментарий не въедливости ради, а для того, чтобы у тех, кто будет использовать этот код, не произошёл «нежданчик»:
Небольшой комментарий не въедливости ради, а для того, чтобы у тех, кто будет использовать этот код, не произошёл «нежданчик»:
Скрытый текст
Эта строка:
и вторая для UIReleased — нет ни проверки на null, ни первичного переноса значения делегата в отдельную переменную (что настоятельно рекомендуется, тем более, что в этом месте гарантирована многопоточность).
Dispatcher.CurrentDispatcher.Invoke(() => UIBlocked());
и вторая для UIReleased — нет ни проверки на null, ни первичного переноса значения делегата в отдельную переменную (что настоятельно рекомендуется, тем более, что в этом месте гарантирована многопоточность).
+3
Ну ещё можно посоветовать заменить
на
и
на
Всё-таки мерить время через DateTime.Now как-то не очень прилично
_lastForegroundTimerTickTime = DateTime.Now;
на
_stopwatch = Stopwatch.StartNew();
и
var totalMilliseconds = (DateTime.Now - _lastForegroundTimerTickTime).TotalMilliseconds;
на
var totalMilliseconds = _stopwatch.ElapsedMilliseconds;
Всё-таки мерить время через DateTime.Now как-то не очень прилично
+6
Так это бенчмарки меряются
Не вижу проблемы измерять сотни миллисекунд через
Stopwatch
'ем.Не вижу проблемы измерять сотни миллисекунд через
DateTime
.-3
Слепые тоже не видят. Но обычно на форумах этим не гордятся.
Погрешность может быть очень большой. На моей памяти правда погрешность была в пределах 16мс, но я не писал под 95 винду. Там это было 55мс.
И самое главное «как долго заняла операция» и «сколько сейчас времени» — это разные задачи. Средства очевидно тоже разные.
Погрешность может быть очень большой. На моей памяти правда погрешность была в пределах 16мс, но я не писал под 95 винду. Там это было 55мс.
И самое главное «как долго заняла операция» и «сколько сейчас времени» — это разные задачи. Средства очевидно тоже разные.
+1
Ну, очевидно, погрешность в 16мс поломает всю задачу определения, повис ли UI-поток.
Осталось выставить CPU Affinity, чтобы поток не скакал между ядрами, и прогреть кэш.
Осталось выставить CPU Affinity, чтобы поток не скакал между ядрами, и прогреть кэш.
+1
То есть человек меняющий константу FreezeTimeLimit должен помнить что если он ее уменьшит до некого N при котором погрешность будет составлять критические для тов. withkittens скажем 10% — ему нужно будет заменить семантически менее корректное решение на семантически более корректное, потому что когда писали изначально — погрешность всех устраивала. Так?
0
Мне удивительно, как вы ратуете за
Stopwatch
с наносекундной точностью, когда как DispatcherTimer
, использующийся в статье, имеет погрешность в 10мс минимум.0
А если пользователь изменит системное время?
+1
У вас может перевод часов на зимнее время и обратно случиться. Или сработать синхронизация времени по NTP. Или пользователь что-то поменяет. DateTime.Now подходит для ответа на вопрос, какое сейчас системное время, но не подходит для ответа на вопрос, сколько прошло времени с заданного момента в прошлом. Для этого используется монотонный счётчик времени, до которого на винде можно достучаться через GetTickCount64 и QueryPerformanceCounter. Второй используется в Stopwatch и использует HPET, но затратнее. Первый даёт меньшую точность, менее затратен, но нужно делать P/Invoke.
См.
github.com/akkadotnet/akka.net/issues/846
github.com/akkadotnet/akka.net/tree/dev/src/core/Akka/Util/MonotonicClock.cs
См.
github.com/akkadotnet/akka.net/issues/846
github.com/akkadotnet/akka.net/tree/dev/src/core/Akka/Util/MonotonicClock.cs
+4
Dispatcher.CurrentDispatcher.Invoke(() => UIBlocked());
Я правильно понимаю, что BackgroundTimerTick вызывается на потоке ThreadPool-а, и вызов в нём Dispatcher.CurrentDispatcher порождает новый диспетчер, связанный с этим потоком? На мой взгляд, это довольно странно. Задачи, выполняемые на потоках пула, не должны влиять на состояние потоков, в которых они запускаются.
+1
Постоянно дергать DateTime.Now плохая идея. Это крайне медленная функция. Для таких задач лучше подходит DateTime.UtcNow
0
(del)
0
Могу рассказать о настоящей серебряной пуле для детекта UI Freeze: есть в Windows Vista+ механизм ETW(Event Tracing for Windows), и готовый провайдер, который умеет кидать сообщения и коллстек, когда какое-нибудь приложение(не обязательно WPF и .NET) в системе не опрашивает очередь сообщений более 200ms. Не нужно лезть в код и инструментировать, создавать два диспетчера. Всё работает в режиме Attach.
С помощью этого механизма dotTrace в режиме Timeline показывает вам те самые UI Freeze на графике и можно поизучать хотспоты на этих участках.
С помощью этого механизма dotTrace в режиме Timeline показывает вам те самые UI Freeze на графике и можно поизучать хотспоты на этих участках.
+2
Обнаружить, что UI «отвис» можно подпиской на событе Dispatcher.Hooks.DispatcherInactive
Думаю обнаружить начало выполнения операций диспетчером можно с использованием других событий Dispatcher.Hooks.
Думаю обнаружить начало выполнения операций диспетчером можно с использованием других событий Dispatcher.Hooks.
0
WPF разрешает создавать несколько UI тредов. Делается это так:
Указанным способом создается несколько блокированных инстансов потока, которые работают синхронно.
Если кому-то понадобится создать несколько реальных UI потоков WPF, то можно подглядеть решение на MSDN.
Я использовал такое решение для создания на WPF индикатора длительных операций, способный крутить анимацию даже при замораживании основного потока UI. ОСТОРОЖНО!!! Корректная реализация IDisposable требуется.
0
Нет, указанный автором способ создает реальные потоки, ограничение тут в другом. Если делать как автор — то можно создать в отдельном потоке только отдельное окно. По вашей же ссылке предлагается создать контрол, лежащий где-то в дереве — но при этом работающий в своем потоке.
0
Sign up to leave a comment.
Детектор блокировок UI в WPF c нотификацией