- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
在我分析的 200+ dump 中,同样会遵循着 28原则,总有那些经典问题总是反复的出现,有很多的朋友就是看了这篇 一个超经典 WinForm 卡死问题的再反思 找到我,说 WinDbg 拦截 System_Windows_Forms_ni System.Windows.Forms.Application+MarshalingControl..ctor 总会有各种各样的问题,而且 windbg 也具有强侵入性,它的附加进程方式让很多朋友望而生畏! 。
这一篇我们再做一次反思,就是如何不通过 WinDbg 找到那个 非主线程创建的控件 ,那到底用什么工具的? 对,就是用 Perfview 的墙钟模式.
我还是用上一篇提到的案例,用 backgroundWorker1 的工作线程去创建一个 Button 控件来模拟这种现象,参考代码如下:
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click_1(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork_1(object sender, DoWorkEventArgs e)
{
Button btn = new Button();
var query = btn.Handle;
}
}
}
一旦控件在工作线程上被创建,代码内部就会实例化 MarshalingControl 和 WindowsFormsSynchronizationContext ,这里就用前者来探究.
那怎么去寻找这个调用栈呢?在 perfview 中有一个 Thread Time 复选框,它可以记录到 Thread 的活动轨迹,在活动轨迹中寻找我们的目标类 MarshalingControl 即可,有了思路之后说干就干,命令行下的参考代码:
PerfView.exe "/DataFile:PerfViewData.etl" /BufferSizeMB:256 /StackCompression /CircularMB:500 /KernelEvents:ThreadTime /NoGui /NoNGenRundown collect
当然也可以在 Focus process 中输入你的进程名来减少 Size,启动 prefview 监控之后,我们打开程序,点击 Button 按钮之后,停止 Prefview 监控,稍等片刻之后我们打开 Thread Time Stacks ,检索我们要的 MarshalingControl 类, 截图如下:
从卦中可以看到如下三点信息:
接下来可以右键选择 Goto -> Goto Item in Callers 看一下它的 Callers 到底都是谁?截图如下:
从卦中可以清晰的看到如下信息:
第一个实例是由 System.Windows.Forms.ScrollableControl..ctor() 触发的.
第二个实例是由 System.Windows.Forms.ButtonBase..ctor() 触发的.
大家可以逐一的去探究,第一个实例是窗体自身的 System.Windows.Forms.Form ,后者就是那个罪魁祸首,卦中信息非常清楚指示了来自于 WindowsFormsApp2.Form1.backgroundWorker1_DoWork_1 ,是不是非常的有意思?
所谓的尽早卡死就是尽可能早的让主线程出现如下调用栈.
0:000:x86> !clrstack
OS Thread Id: 0x4eb688 (0)
Child SP IP Call Site
002fed38 0000002b [HelperMethodFrame_1OBJ: 002fed38] System.Threading.WaitHandle.WaitOneNative(System.Runtime.InteropServices.SafeHandle, UInt32, Boolean, Boolean)
002fee1c 5cddad21 System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle, Int64, Boolean, Boolean)
002fee34 5cddace8 System.Threading.WaitHandle.WaitOne(Int32, Boolean)
002fee48 538d876c System.Windows.Forms.Control.WaitForWaitHandle(System.Threading.WaitHandle)
002fee88 53c5214a System.Windows.Forms.Control.MarshaledInvoke(System.Windows.Forms.Control, System.Delegate, System.Object[], Boolean)
002fee8c 538dab4b [InlinedCallFrame: 002fee8c]
002fef14 538dab4b System.Windows.Forms.Control.Invoke(System.Delegate, System.Object[])
002fef48 53b03bc6 System.Windows.Forms.WindowsFormsSynchronizationContext.Send(System.Threading.SendOrPostCallback, System.Object)
002fef60 5c774708 Microsoft.Win32.SystemEvents+SystemEventInvokeInfo.Invoke(Boolean, System.Object[])
002fef94 5c6616ec Microsoft.Win32.SystemEvents.RaiseEvent(Boolean, System.Object, System.Object[])
002fefe8 5c660cd4 Microsoft.Win32.SystemEvents.OnUserPreferenceChanged(Int32, IntPtr, IntPtr)
002ff008 5c882c98 Microsoft.Win32.SystemEvents.WindowProc(IntPtr, Int32, IntPtr, IntPtr)
...
如果不能尽早的让程序卡死,那你就非常被动,因为在真实的案例实践中,这个 t1 时间的 new button,可能在 t10 时间因为某些操作才会出现程序卡死,所以你会被迫用 prefview 一直监视,而一直监视就会导致生成太多的 etw 事件,总之很搞的.
先感谢下上海的 包老师 提供的一段很棒的脚本,也经过了老师实测 让这个问题解决起来更加完美 ❤ 。
这里我用 ILSpy 反编译一下这个执行程序,完整代码如下:
// Freezer.FreezerForm
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Freezer;
public class FreezerForm : Form
{
private Button btnFreezeEm;
private Container components = null;
private const uint WM_SETTINGCHANGE = 26u;
private const uint HWND_BROADCAST = 65535u;
private const uint SMTO_ABORTIFHUNG = 2u;
public FreezerForm()
{
InitializeComponent();
}
protected override void Dispose(bool disposing)
{
if (disposing && components != null)
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
btnFreezeEm = new System.Windows.Forms.Button();
SuspendLayout();
btnFreezeEm.Location = new System.Drawing.Point(89, 122);
btnFreezeEm.Name = "btnFreezeEm";
btnFreezeEm.Size = new System.Drawing.Size(115, 23);
btnFreezeEm.TabIndex = 0;
btnFreezeEm.Text = "Freeze 'em!";
btnFreezeEm.Click += new System.EventHandler(btnFreezeEm_Click);
AutoScaleBaseSize = new System.Drawing.Size(6, 15);
base.ClientSize = new System.Drawing.Size(292, 267);
base.Controls.Add(btnFreezeEm);
base.Name = "FreezerForm";
Text = "Freezer";
ResumeLayout(false);
}
[DllImport("user32.dll")]
private static extern uint SendMessageTimeout(uint hWnd, uint msg, uint wParam, string lParam, uint flags, uint timeout, out uint result);
[STAThread]
private static void Main()
{
Application.Run(new FreezerForm());
}
private void btnFreezeEm_Click(object sender, EventArgs e)
{
try
{
Cursor = Cursors.WaitCursor;
SendMessageTimeout(65535u, 26u, 0u, "Whatever", 2u, 5000u, out var _);
}
finally
{
Cursor = Cursors.Arrow;
}
}
}
这个脚本供大家参考吧,这里要提醒一下,我实测了下需要在运行时需要反复点以及最小最大话可能会遇到一次,不管怎么说还是非常好的宝贵资料.
关于对 非主线程创建控件 的问题,这已经是第三篇思考了,希望后续不要再写这个主题了.
最后此篇关于一个超经典WinForm卡死问题的最后一次反思的文章就讲到这里了,如果你想了解更多关于一个超经典WinForm卡死问题的最后一次反思的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
在C#中,我可以通过base关键字访问基类,在java中,我可以通过super关键字访问它。在delphi中如何做到这一点?假设我有以下代码: type TForm3 = class(TF
在这件事上我已经把头撞到墙上好几次了。所以我希望在正确的方向上得到一点帮助。 我有一张 table ,上面有订单,一张 table 上有火车,一张 table 上有航类,一张 table 上有巴士。每
在 Python 中,假设我有以下代码: class SuperClass(object): def __init__(self, x): self.x = x
我希望这个 subview 扩展到它的父 View 之外,但是父 View 的边框正在切入 subview 。有没有办法防止这种情况? class TheView : UIView { let
我有一个标准的高斯函数,看起来像这样: def gauss_fnc(x, amp, cen, sigma): return amp * np.exp(-(x - cen) ** 2 / (2
例如,我有下一个类,带有有界类型参数: public class ItemContainer { void addItems(List items); } 在另一个带有参数的类中使用: pub
如何将此设置转换为命令? 结果如下: // Manual Compression (see the image above) Compressed Size: 12,647,451 bytes //
请建议在应用继承时如何使用@Wither/@With。 我有一个抽象类Parent和具体的Child。 Child 应该是不可变的。将 @Wither 放在两者上会给我两个错误: 构造函数 Child
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我正在尝试向服务器(虚拟托管)发出 HTTP2 请求,该服务器根据主机 header 值 (SNI) 提供 SSL 证书。 # conn = hyper.HTTP20Connection('h
我有一个应用程序,必须将大约 1300 万行、大约 10 个平均长度的字符串插入到嵌入式 HSQLDB 中。我一直在调整一些东西(批量大小、单线程/多线程、缓存/非缓存表、MVCC 事务、log_si
我想定义一个函数f(x, t::Type)根据 isa(x, t) 是否执行不同的行为.假设我想调用b1(x)如果是,b2(x)除此以外。 我知道我可以像这样在运行时进行动态检查: function
我正在使用 Hyper-V WMI Provider在 Hyper-V 中导入虚拟机,特别是使用 ImportVirtualSystemEx Msvm_VirtualSystemManagementS
这几个星期以来一直困扰着我,我没有结束对它的研究,因为我目前重载并且它让我落后于第一年的 CS (opengl) 大学类(class),这首先让我研究了这个:如何只用一个 for 循环绘制立方体的所有
我正在我的计算机(操作系统:Windows 8)上开发一个 WP8 应用程序。我需要安装一个 VM 才能拥有 linux。同时我需要使用我的 Windows Phone 模拟器。 我下载了 VMWar
我是一名优秀的程序员,十分优秀!