【发布时间】:2021-04-18 00:39:03
【问题描述】:
我遇到了一些我完全不明白的事情。 在我的应用程序中,我有几个线程都将项目添加(和删除)到共享集合(使用共享锁)。 UI 线程使用一个计时器,并且在每个滴答声中它使用集合来更新它的 UI。
由于我们不希望 UI 线程长时间持有锁而阻塞其他线程,所以我们这样做的方式是,首先获取锁,复制集合,然后释放锁定,然后处理我们的副本。 代码如下所示:
public void GUIRefresh()
{
///...
List<Item> tmpList;
lock (Locker)
{
tmpList = SharedList.ToList();
}
// Update the datagrid using the tmp list.
}
虽然它运行良好,但我们注意到有时应用程序会变慢,当我们设法捕获堆栈跟踪时,我们看到了:
....
at System.Windows.Forms.DataGrid.OnPaint(PaintEventArgs pe)
at MyDataGrid.OnPaint(PaintEventArgs pe)
at System.Windows.Forms.Control.PaintWithErrorHandling(PaintEventArgs e, Int16 layer, Boolean disposeEventArgs)
at System.Windows.Forms.Control.WmPaint(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Threading.Monitor.Enter(Object obj)
at MyApplication.GuiRefresh()
at System.Windows.Forms.Timer.OnTick(EventArgs e)
at System.Windows.Forms.Timer.TimerNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
....
请注意,输入锁 (Monitor.Enter) 之后是 NativeWindow.Callback,它会导致 OnPaint。
这怎么可能? UI 线程是否被劫持以检查其消息泵?那有意义吗?还是这里有别的东西?
有没有办法避免它?我不希望从锁中调用 OnPaint。
谢谢。
【问题讨论】:
-
如何捕获堆栈跟踪?
-
我们持有对线程的引用。然后我们做 thread.Suspend();日志(新 StackTrace(线程,真).ToString());线程.Resume();
-
在 Visual Studio 中的 Debug 下运行我的应用程序时,我得到了类似的堆栈跟踪。当 DataGridView 开始非常缓慢地绘制时,我刚刚点击了暂停(这样您就可以看到每个单独的单元格突然被绘制)并注意到它正在处理来自
lock的OnPaint(我的堆栈跟踪说“管理到本机转换”而不是“Monitor.Enter()”)在OnScroll处理程序中。我认为速度缓慢是由于OnScroll处理程序未完成导致一些魔法(例如,缓存值?)丢失,并且 this 导致OnPaint运行缓慢。
标签: c# winforms multithreading