WPF起步(中) --- 的main thread跟render thread如何通信

来源:互联网 发布:安平淘宝贝高瞻幼儿园 编辑:程序博客网 时间:2024/05/20 22:40

接下来的问题是,WPF如何通知这个worker thread什么时候应该Render,这个thread跟main thread是如何通信的。要回答这个问题,我的做法是:

1. 选择一个非常简单的WPF程序,观察该程序idle状态时候每个thread的状态
2. 在Render thread上设定断点,当Render thread触发的时候,再次检查各个thread的状态。

于是我做了一个只有简单button的WPF程序,开始观察:
Idle 状态:

0:000> !clrstack
OS Thread Id: 0x145c (0)
ESP       EIP    
0012f354 7c8285ec [ComPlusMethodFrameGeneric: 0012f354] MS.Win32.UnsafeNativeMethods+ITfMessagePump.GetMessageW(System.Windows.Interop.MSG ByRef, Int32, Int32, Int32, Boolean ByRef)
0012f374 56d61937 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
0012f3c0 56d617e3 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
0012f410 56d616c7 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
0012f42c 56d6162d System.Windows.Threading.Dispatcher.Run()
0012f438 5533dce0 System.Windows.Application.RunInternal(System.Windows.Window)
0012f464 5533db15 System.Windows.Application.Run(System.Windows.Window)
0012f474 5533d766 System.Windows.Application.Run()
0012f47c 00f000ad WpfApplication1.App.Main()
0012f69c 79e7c74b [GCFrame: 0012f69c]

   6  Id: 1874.103c Suspend: 1 Teb: 7ffd9000 Unfrozen
ChildEBP RetAddr 
0362fed0 7c827d0b ntdll!KiFastSystemCallRet
0362fed4 77e61d1e ntdll!NtWaitForSingleObject+0xc
0362ff44 77e61c8d KERNEL32!WaitForSingleObjectEx+0xac
0362ff58 53195fb8 KERNEL32!WaitForSingleObject+0x12
0362ff88 53195fd9 MilCore!CPartitionManager::GetWork+0x1a5
0362ffa0 53208d55 MilCore!CPartitionThread::Run+0x1a
0362ffb8 77e64829 MilCore!CPartitionThread::ThreadMain+0x1e
0362ffec 00000000 KERNEL32!BaseThreadStart+0x34

很好,非常简单。Render thread停留在MilCore!CPartitionManager::GetWork函数上面。我只需要在这个函数的返回值上放断点,就应该能看到该函数什么时候被唤醒,于是运行:

0:000> bp 53195fd9
0:000> g

这个时候在WPF程序上随便动一下鼠标,看到:

Breakpoint 0 hit
eax=00000001 ebx=00187418 ecx=5334e0d0 edx=0000034a esi=00187418 edi=00000001
eip=53195fd9 esp=0362ff94 ebp=0362ffa0 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
MilCore!CPartitionThread::Run+0x1a:
53195fd9 8bf8            mov     edi,eax

立刻到main thread看,发现:

0:000> !clrstack
OS Thread Id: 0x145c (0)
ESP       EIP    
0012e380 7c8285ec [InlinedCallFrame: 0012e380] System.Windows.Media.Composition.MilCoreApi.MilComposition_SyncFlush(IntPtr)
0012e37c 5395bd29 System.Windows.Media.Composition.DUCE+Channel.SyncFlush()
0012e3bc 53967ae0 System.Windows.Media.Composition.DUCE+CompositionTarget.UpdateWindowSettings(ResourceHandle, RECT, System.Windows.Media.Color, Single, System.Windows.Media.Composition.MILWindowLayerType, System.Windows.Media.Composition.MILTransparencyFlags, Boolean, Boolean, Boolean, Int32, Channel)
0012e468 539678f8 System.Windows.Interop.HwndTarget.UpdateWindowSettings(Boolean, System.Collections.Generic.List`1<ChannelSet>)
0012e610 53972033 System.Windows.Interop.HwndTarget.UpdateWindowPos(IntPtr)
0012e684 5396969a System.Windows.Interop.HwndTarget.HandleMessage(Int32, IntPtr, IntPtr)
0012e6b4 53965a9a System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
0012e6d8 56d5efc3 MS.Win32.HwndWrapper.WndProc(IntPtr, Int32, IntPtr, IntPtr, Boolean ByRef)
0012e714 56d5ef2a MS.Win32.HwndSubclass.DispatcherCallbackOperation(System.Object)
0012e720 56d5ee32 System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Boolean)
0012e73c 56d5ed67 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(System.Object, System.Delegate, System.Object, Boolean, System.Delegate)
0012e784 56d5e7c7 System.Windows.Threading.Dispatcher.InvokeImpl(System.Windows.Threading.DispatcherPriority, System.TimeSpan, System.Delegate, System.Object, Boolean)
0012e7d4 56d5e65d System.Windows.Threading.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority, System.Delegate, System.Object)
0012e7f8 56d5df84 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)
0012eb70 003a230c [InlinedCallFrame: 0012eb70] MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr, IntPtr, Int32, IntPtr, IntPtr)
0012eb6c 56d5f0fe MS.Win32.HwndSubclass.DefWndProcWrapper(IntPtr, Int32, IntPtr, IntPtr)
0012ecfc 003a230c [NDirectMethodFrameStandalone: 0012ecfc] MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr, IntPtr, Int32, IntPtr, IntPtr)
0012ed18 56d5dfe0 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)
0012f078 003a230c [InlinedCallFrame: 0012f078] MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr, IntPtr, Int32, IntPtr, IntPtr)
0012f074 56d5f0fe MS.Win32.HwndSubclass.DefWndProcWrapper(IntPtr, Int32, IntPtr, IntPtr)
0012f204 003a230c [NDirectMethodFrameStandalone: 0012f204] MS.Win32.UnsafeNativeMethods.CallWindowProc(IntPtr, IntPtr, Int32, IntPtr, IntPtr)
0012f220 56d5dfe0 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr, Int32, IntPtr, IntPtr)
0012f3b0 003a230c [NDirectMethodFrameStandalone: 0012f3b0] MS.Win32.UnsafeNativeMethods.DispatchMessage(System.Windows.Interop.MSG ByRef)
0012f3c0 56d61824 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
0012f410 56d616c7 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
0012f42c 56d6162d System.Windows.Threading.Dispatcher.Run()
0012f438 5533dce0 System.Windows.Application.RunInternal(System.Windows.Window)
0012f464 5533db15 System.Windows.Application.Run(System.Windows.Window)
0012f474 5533d766 System.Windows.Application.Run()
0012f47c 00f000ad WpfApplication1.App.Main()
0012f69c 79e7c74b [GCFrame: 0012f69c]

立刻打开reflector,研究上面callstack中相关的函数,得到的信息是:
WPF通过System.Windows.Media.Composition.DUCE相关函数跟worker thread通信,具体来说是通过kernel object来实现的。仔细研究了DUCE内相关函数后,发现这个东西相当有货,从暴露出来的函数名字,就可以猜测到UI thread和render thread的交互模型。应该是UI thread准备好足够的数据后,然后发送请求通知Render thread,然后Render thread拿到对应的请求完成工作。从名字上看,下面这个函数就非常的值得关注:

System.Windows.Media.Composition.DUCE+Channel.SendCommand

接下来的任务就是通过调试器来进一步分析UI thread中具体发生了些什么事情。 什么时候通知render thread画图