UI僵死分析
原因剖析
UI僵死无非只是因为UI线程因繁忙而无法去接受用户的响应。详细说来内在原因有以下两个:
- 正常的业务代码写在UI线程中执行,业务代码的任务繁重导致UI线程无法分身去接受用户的界面输入
- UI控件在非UI线程中创建。原因如下如述:
- 每一个UI控件创建后都向SystemEvents注册UserPreferenceChanged事件,并且创建了控件的线程会被自动安装WindowsFormsSynchronizationContext作为其同步上下文
- 系统默认在UI线程里创建一个隐藏窗口“.NET-BroadcastEventWindow”来获取SystemEvents相关的系统消息
- 此隐藏窗口获取到消息后通过系统的PostMessage方法向注册了此事件的各UI控件发送通知并等待(注意不是通过SendMessage)
- 若某UI控件未创建在UI线程上,因为其创建控件的线程不会监视和获取本线程的消息队列中的消息,所以UI线程的PostMessage方法会一直等待,UI呈现僵死状态
PostMessage
- 将消息送至目标window所在线程(可通过系统API获取控件的句柄所属的线程)的“Posted Message Queue”消息队列,消息称为列队型(queued)型消息
- Control.Invoke与Control.BeginInvoke都调用PostMessage(相比SendMessage可防止死锁),区别是前者会使用WaitForWaitHandle来等待消息处理完毕
SendMessage
- 将消息送至目标window所在线程的“Sent Message Queue”消息队列,但消息称为非列队型(Non-queued)消息
- 发送线程调用SendMessage后会挂起并等待返回,如果期间有其他线程发消息给这个发送线程,它可以响应,但仅限于非队列型(Non-queued)消息
- WH_CALLWNDPROC钩子用于监视SendMessage调用
异常发生后如何诊断
诊断的目的是要确定引发了UI线程繁忙的原因。
- 若是因为上述第1点原因,即因为正常的业务代码在UI线程中跑的话,直接用VS等调试工具看一下UI线程的堆栈即可;
- 若若因为上述第2点原因,即因为在非UI线程中创建了UI控件的话,那得先找出此控件。UI线程里此控件因为触发了SynchronizationContext.Send方法而冻结。
- 使用spy++可以直接查看活动的后台线程上是否有控件。不过若线程将控件创建出来放在堆内存上后线程就消亡了的话,那就无法看到了。
- 使用Windbg:
- 获取UI线程堆栈一看便知控件的类名,对照着Windbg的“!dso”命令结果找到此控件的地址,再查找其引用。若UI线程中显示的控件类型因其为内部的子控件且为通用类型而无法直接定位代码的话,那么可以尝试追溯找到此控件的父控件。
- 也可获取让UI冻结的WindowsFormSynchronizationContext,再通过以下方式找出目标托管线程的ID。不过因为托管线程在消亡后的ID可以被重复使用,所以通过此方式找到的托管线程ID可能是已经消亡的线程的ID,所以之后要找到目标Thread对象再比较其Thread.m_ExecutionContext._syncContext是否不为空且正为让UI冻结的WindowsFormSynchronizationContext。
- 使用“!do <synchronizationContext对象地址>”命令显示其数据结构,从成员destinationThreadRef获取指定了创建控件的目标线程的WeakReference对象地址
- 使用“!dumpobject <WeakReference对象地址>”显示其数据结构,从成员m_handle获取创建控件的目标线程的句柄
- 使用“dd <目标线程的句柄> L1”命令显示此句柄中包含的线程地址
- 使用“!dumpobject <目标线程地址>”命令显示目标线程的数据结构,从成员m_ManagedThreadId获取目标线程的托管线程号
- 使用“?0n<托管线程号>”命令获取托管线程号的16进制数
防患于未然
写代码时应该遵循这条原则:保证线程安全,尤其是不该在非UI线程上直接进行UI操作,包括控件的创建。
不过团队水平参差不齐,即使是一些老手也难免犯错。
所以如果能够拦截控件创建的过程,那么就可以通过Windows API根据此控件的句柄获取其在运行的线程号,看是否就是主UI线程号来输出日志,以在调试阶段解决问题。
有以下两种途径:
拦截Winodws Message。通过创建Global Hook拦截所有线程的窗口创建消息。
拦截Windows API。通过拦截各线程对窗口创建的API的调用。
使用windbg拉截windows api的调用前不要忘了为其加载符号。如:srv*c:\symbols*http://msdl.microsoft.com/download/symbols
本人通过EasyHook开源库使用了第2种方法,即拦截对WindowsAPI的调用完成了工具的创建,截图如下:

参考
Debugging Windows Forms Application Hangs During SystemEvents.UserPreferenceChanged
Windows Forms application freezes when system settings are changed or the workstation is locked
Mysterious Hang or The Great Deception of InvokeRequired
WinForm二三事(三)Control.Invoke&Control.BeginInvoke
一千个是什么 - Windows消息机制(Windows Messaging)
Using Window Messages to Implement Global System Hooks in C#
Windows下Hook API技术 inline hook
Change C# Class object to System.IntPtr
UI僵死分析的更多相关文章
- 对石家庄铁道大学网站UI的分析
作为我们团队的PM,老师对我们提出了一些额外的要求,所以我发表这篇博客来谈一下对石家庄铁道大学网站UI的分析. 首先,PM 对项目所有功能的把握, 特别是UI.最差的UI, 体现了 ...
- 针对某一网站的UI进行分析
本周课上教学通过对PM(项目经理)的学习,我了解到PM 对项目所有功能的把握, 特别是有关的UI内容.最差的UI, 体现了团队的组织架构:其次, 体现了产品的内部结构:最好, 体现了用户的自然需求. ...
- Android recovery UI实现分析
Android recovery模式为何物? 关于这个问题, baidu上已经有无数的答案.不理解的朋友先补习一下. 从纯技术角度来讲, recovery和android本质上是两个独立的rootfs ...
- Android 性能优化(2)性能工具之「Hierarchy Viewer 」Optimizing Your UI:分析哪个view有性能问题,查看屏幕上某像素点的坐标,颜色等
Optimizing Your UI In this document Using Hierarchy Viewer Running Hierarchy Viewer and choosing a w ...
- Reveal UI 分析工具分析手机 App
上篇文章介绍了: Reveal UI 分析工具简单使用 这里介绍如何使用 Reveal UI 分析工具来进行手机 App UI 界面的分析. 前提准备: (1)已安装 Reveal 的 Mac (2) ...
- 对石家庄铁道大学网站首页进行UI分析
对石家庄铁道大学网站首页进行UI界面分析首先,铁道大学的网页首页分为图文热点,学校新闻,校内公告,媒体看铁大,学术咨询等等模块.通过分析这些模块,可以看出,学校网站首页针对的使用对象有很多,包括学校领 ...
- BookStore示例项目---菜单栏UI分析
部署 参照 ABP示例项目BookStore搭建部署 项目解构 1).动态脚本代理 启动项目时,默认会调用两个接口 /Abp/ApplicationConfigurationScript /Abp/S ...
- 转——Android应用开发性能优化完全分析
[工匠若水 http://blog.csdn.net/yanbober 转载请注明出处.] 1 背景 其实有点不想写这篇文章的,但是又想写,有些矛盾.不想写的原因是随便上网一搜一堆关于性能的建议,感觉 ...
- DIY Ruby CPU 分析——Part I
[编者按]原文作者 Emil Soman,Rubyist,除此之外竟然同时也是艺术家,吉他手,Garden City RubyConf 组织者.本文是DIY Ruby CPU Profiling 的第 ...
随机推荐
- ABP启动配置
ABP启动配置 返回ABP系列 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ASP.NET Boilerplate是一个用最佳实践和流行 ...
- centos6安装bt工具transmission
centos6 install transmission 1. 安装所需的组件: yum -y install gcc gcc-c++ m4 make automake libtool gettex ...
- Linux I/O 重定向详解及应用实例
Linux I/O 重定向详解及应用实例 简解 > 输出 < 输入 >> 追加 & [> | < | >>]之前:输入输出; ls /dev & ...
- md5增加指定的加密规则,进行加密
import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.securit ...
- 11gR2(11.2) RAC TAF Configuration for Admin and Policy Managed Databases (文档 ID 1312749.1)
In this Document Purpose _afrLoop=1459323732561579&id=1312749.1&displayIndex=10&_afr ...
- 给你的站点加入 console.js
本文仅先给使用console调试的FE同学,假设你还不知道console是什么.或者还停留在alert阶段,那就不要浪费时间了,say bay bay! 你是否试程序的过程中用过console.log ...
- Jedi项目,还真得好好看看,有许多控件和新封装的API(Delphi里面没有)
以前没有重视 http://www.delphi-jedi.org/ https://github.com/project-jedi https://sourceforge.net/projects/ ...
- Face-landmarks-detection-benchmark 人脸特征定位网站汇总
源地址:https://www.douban.com/note/525032729/ https://github.com/delphifirst/FaceXhttps://github.com/ ...
- 在Delphi中使用C++对象(两种方法,但都要改造C++提供的DLL)
Delphi是市场上最好的RAD工具,但是现在C++占据着主导地位,有时针对一个问题很难找到Delphi或Pascal的解决方案.可是却可能找到了一个相关的C++类.本文描述几种在Delphi代码中使 ...
- 与众不同 windows phone (32) - Communication(通信)之任意源组播 ASM(Any Source Multicast)
原文:与众不同 windows phone (32) - Communication(通信)之任意源组播 ASM(Any Source Multicast) [索引页][源码下载] 与众不同 wind ...