WPF用户界面线程在鼠标抬起事件触发动画时会短暂冻结。

3

我有一个不断进行动画的WPF应用程序。特别是一个交互(mouseup)和动画似乎会冻结UI线程,足够长时间以至于非常明显。

并发可视化器显示我的UI线程被阻塞了300-500毫秒。它还显示了在阻塞时的堆栈和解除阻塞的堆栈:

冻结时的主线程堆栈
类别 = 同步
延迟 = 495.2472毫秒

ntoskrnl.exe!SwapContext_PatchXRstor
ntoskrnl.exe!KiSwapContext
ntoskrnl.exe!KiCommitThreadWait
ntoskrnl.exe!KeWaitForSingleObject
ntoskrnl.exe!NtWaitForSingleObject
ntoskrnl.exe!KiSystemServiceCopyEnd
ntdll.dll!NtWaitForSingleObject
kernelbase.dll!WaitForSingleObjectEx
wpfgfx_v0400.dll!CMilConnection::SynchronizeChannel
wpfgfx_v0400.dll!CMilChannel::SyncFlush
wpfgfx_v0400.dll!MilComposition_SyncFlush
clr.dll!DoNDirectCallWorker
presentationcore.dll!System.Windows.Media.Composition.DUCE+Channel.**SyncFlush**
presentationcore.dll!System.Windows.Media.MediaContext.NotifyChannelMessage
presentationcore.dll!System.Windows.Media.MediaContextNotificationWindow.MessageFilter
windowsbase.dll!MS.Win32.HwndWrapper.WndProc
windowsbase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation
windowsbase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall
windowsbase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen
windowsbase.dll!System.Windows.Threading.Dispatcher.WrappedInvoke
windowsbase.dll!System.Windows.Threading.Dispatcher.InvokeImpl
windowsbase.dll!MS.Win32.HwndSubclass.SubclassWndProc
windowsbase.dll!dynamicClass.IL_STUB_ReversePInvoke
clr.dll!UMThunkStubAMD64
user32.dll!UserCallWinProcCheckWow
user32.dll!DispatchMessageWorker
clr.dll!DoNDirectCall__PatchGetThreadCall
windowsbase.dll!dynamicClass.IL_STUB_PInvoke
windowsbase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl
presentationframework.dll!System.Windows.Application.RunInternal
presentationframework.dll!System.Windows.Application.Run
xqstream.windows.exe!IQ.IR.Stream.WPF.App.Main

解除阻塞的堆栈:

ntoskrnl.exe! ?? ?? ::FNODOBFM::`string'
ntoskrnl.exe!NtSetEvent
ntoskrnl.exe!KiSystemServiceCopyEnd
ntdll.dll!ZwSetEvent
kernelbase.dll!SetEvent
wpfgfx_v0400.dll!CMilConnection::PostMessageToClient
wpfgfx_v0400.dll!CMilServerChannel::SignalFinishedFlush
wpfgfx_v0400.dll!CComposition::FlushChannels
wpfgfx_v0400.dll!CPartitionThread::RenderPartition
wpfgfx_v0400.dll!CPartitionThread::Run
wpfgfx_v0400.dll!CPartitionThread::ThreadMain

主线程正在等待后台线程。该后台线程正在执行许多任务,但该堆栈占用了大约3/4的时间: 阻塞线程最大的任务: 类别 = I/O
延迟 = 347.5122 毫秒
ntoskrnl.exe!SwapContext_PatchXRstor
ntoskrnl.exe!KiSwapContext
ntoskrnl.exe!KiCommitThreadWait
ntoskrnl.exe!KeWaitForSingleObject
dxgmms1.sys!VIDMM_GLOBAL::CloseOneAllocation
dxgmms1.sys!VidMmCloseAllocation
dxgkrnl.sys!DXGDEVICE::DestroyAllocations
dxgkrnl.sys!DXGDEVICE::ProcessTerminationList
dxgkrnl.sys!DXGDEVICE::TerminateAllocations
dxgkrnl.sys!DXGDEVICE::DestroyAllocation
dxgkrnl.sys!DxgkDestroyAllocation
win32k.sys!NtGdiDdDDIDestroyAllocation
ntoskrnl.exe!KiSystemServiceCopyEnd
gdi32.dll!ZwGdiDdDDIDestroyAllocation
d3d9.dll!DeallocateCB
dlumd64.dll![dlumd64.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
nvd3dumx.dll![nvd3dumx.dll]
dlumd64.dll![dlumd64.dll]
dlumd64.dll![dlumd64.dll]
d3d9.dll!DdBltLH
d3d9.dll!CSwapChain::PresentMain
d3d9.dll!CSwapChain::Present
wpfgfx_v0400.dll!CD3DDeviceLevel1::PresentWithD3D
wpfgfx_v0400.dll!CD3DDeviceLevel1::Present
wpfgfx_v0400.dll!CHwDisplayRenderTarget::PresentInternal
wpfgfx_v0400.dll!CHwDisplayRenderTarget::Present
wpfgfx_v0400.dll!CHwHWNDRenderTarget::Present
wpfgfx_v0400.dll!CDesktopRenderTarget::Present
wpfgfx_v0400.dll!CDesktopHWNDRenderTarget::Present
wpfgfx_v0400.dll!CSlaveHWndRenderTarget::Present
wpfgfx_v0400.dll!CRenderTargetManager::Present
wpfgfx_v0400.dll!CComposition::Present
wpfgfx_v0400.dll!CPartitionThread::PresentPartition
wpfgfx_v0400.dll!CPartitionThread::Run
wpfgfx_v0400.dll!CPartitionThread::ThreadMain

我已经阅读了SyncFlush()函数在多个MilCore功能之后被调用,并且似乎会立即处理已发送的更改(感谢Ray Burns)。

我希望消除这个冻结,但我也希望在此情况下开发对WPF正在做什么的更好理解,以便我可以建立更好的WPF工作模型。


1
如果您能创建一个复制样本,那就太理想了。 - Adam Sills
2个回答

3

从前后的堆栈跟踪来看,很明显主线程正在等待某些内容完全刷新。然而我不知道 SyncFlush 是什么或者你该如何解决它。

从长的 IO 堆栈跟踪来看,问题可能是 Nvidia 驱动程序。WPF(wpfgfx_v0400)调用 DirectX 9(d3d9),后者又调用 Nvidia 驱动程序(nvd3dumx)。

现在有可能所有平台都表现出了正确的行为,并且按照你的代码所指示的做了。也有可能 WPF、DirectX 9 或 Nvidia 驱动程序存在 bug。然而大多数情况下,问题出在调用代码上。

我假设这个应用程序有一个鼠标松开事件处理程序。如果是这样的话,查看它正在做什么(或者它是如何干扰动画的)将是性能问题最有可能的源头。


-2

当您的UI线程被阻塞时,通常是因为您在事件上执行的某些操作没有在自己的线程中运行。我建议检查mouseup事件是否存在可以推到后台线程并允许UI线程自由调度其他事件的长时间运行操作。

庞大的堆栈跟踪几乎无法确定您真正的问题所在。


Tejs,我有点困惑。这是否意味着我不应该信任并发可视化器?它向我展示了一个500毫秒的任务,将其分类为同步,并显示了一个包含WaitForSingleObject的堆栈,并指向正在等待的线程。您似乎在暗示问题不是同步,而实际上是在主线程上进行的长时间运行任务。除非我误解了您的意思,否则这与并发可视化器的指示完全相反。 - Tristan
如果不知道你的鼠标抬起事件的代码,就无法确定。如果鼠标抬起确实使用了等待信号量,那么它听起来像是有可能阻塞UI线程的线程代码。因此,并发可视化工具提供给你数据,没错。但是,除了说等待操作正在阻塞线程之外,这些数据并没有什么用处。 - Tejs
如果问题出现在MouseUp事件处理程序中,我不应该期望在WaitForSingleObject的堆栈下看到MouseUp事件处理程序吗?为了确认它不是MouseUp处理程序,我计时了处理程序的执行时间,结果小于0毫秒。 - Tristan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接