我正在研究一个复杂的WPF应用程序,该应用程序在生产中挂起了几天。除了GUI线程之外,还有一个线程将数据填充到模型并绑定(bind)到网格并触发INotifyPropertyChanged.PropertyChanged
事件。我编写了一个脚本,将MDbg附加到挂起的进程并转储线程的当前堆栈跟踪。在找到死锁的原因时,它很有用,但这次却无济于事。
正在更新模型的线程在获取ReadLock
时停止:
Thread [#:8]
*0. WindowsBase.dll#0!MS.Internal.ReaderWriterLockWrapper.get_ReadLock() (source line information unavailable)
1. WindowsBase.dll#0!System.ComponentModel.PropertyChangedEventManager.OnPropertyChanged(sender=<...>, args=System.ComponentModel.PropertyChangedEventArgs) (source line information unavailable)
2. ( ... firing PropertyChanged event ... )
GUI线程发生了相同的事情:
Thread [#:0]
*0. WindowsBase.dll#0!MS.Internal.ReaderWriterLockWrapper.get_ReadLock() (source line information unavailable)
1. WindowsBase.dll#0!System.ComponentModel.PropertyChangedEventManager.OnPropertyChanged(sender=MyCompany.Windows.ViewModel.Window.WindowViewModel, args=System.ComponentModel.PropertyChangedEventArgs) (source line information unavailable)
2. MyCompany.Windows.Contracts.dll#0!MyCompany.Windows.ViewModel.Model.ViewModelItemBase.NotifyPropertyChanged(propertyName="IsActive") (source line information unavailable)
3. MyCompany.Windows.Contracts.dll#0!MyCompany.Windows.ViewModel.Window.WindowViewModel.set_IsActive(value=True) (source line information unavailable)
4. MyCompany.Windows.dll#0!MyCompany.Windows.ViewModel.Window.IsActiveBinding.OnWindowIsActiveChanged(sender=MyCompany.Xpf.Views.XpfRibbonShell.XpfRibbonShellView, e=System.EventArgs) (source line information unavailable)
5. WindowsBase.dll#0!MS.Internal.ComponentModel.PropertyChangeTracker.OnPropertyInvalidation(d=<N/A>, args=<N/A>) (source line information unavailable)
6. WindowsBase.dll#0!System.Windows.DependentList.InvalidateDependents(source=<N/A>, sourceArgs=<N/A>) (source line information unavailable)
7. WindowsBase.dll#0!System.Windows.DependencyObject.NotifyPropertyChange(args=<N/A>) (source line information unavailable)
8. WindowsBase.dll#0!System.Windows.DependencyObject.UpdateEffectiveValue(entryIndex=<N/A>, dp=<N/A>, metadata=<N/A>, oldEntry=<N/A>, newEntry=<N/A>, coerceWithDeferredReference=<N/A>, coerceWithCurrentValue=<N/A>, operationType=<N/A>) (source line information unavailable)
9. WindowsBase.dll#0!System.Windows.DependencyObject.SetValueCommon(dp=<N/A>, value=<N/A>, metadata=<N/A>, coerceWithDeferredReference=<N/A>, coerceWithCurrentValue=<N/A>, operationType=<N/A>, isInternal=<N/A>) (source line information unavailable)
10. WindowsBase.dll#0!System.Windows.DependencyObject.SetValue(key=<N/A>, value=<N/A>) (source line information unavailable)
11. PresentationFramework.dll#0!System.Windows.Window.HandleActivate(windowActivated=<N/A>) (source line information unavailable)
12. PresentationFramework.dll#0!System.Windows.Window.WmActivate(wParam=<N/A>) (source line information unavailable)
13. PresentationFramework.dll#0!System.Windows.Window.WindowFilterMessage(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
14. PresentationCore.dll#0!System.Windows.Interop.HwndSource.PublicHooksFilterMessage(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
15. WindowsBase.dll#0!MS.Win32.HwndWrapper.WndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
16. WindowsBase.dll#0!MS.Win32.HwndSubclass.DispatcherCallbackOperation(o=<N/A>) (source line information unavailable)
17. WindowsBase.dll#0!System.Windows.Threading.ExceptionWrapper.InternalRealCall(callback=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
18. WindowsBase.dll#0!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(source=System.Windows.Threading.Dispatcher, method=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<null>) (source line information unavailable)
19. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.WrappedInvoke(callback=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<N/A>) (source line information unavailable)
20. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.InvokeImpl(priority=<N/A>, timeout=<N/A>, method=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
21. WindowsBase.dll#0!MS.Win32.HwndSubclass.SubclassWndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>) (source line information unavailable)
[IL Method without Metadata]
22. WindowsBase.dll#0!MS.Internal.ReaderWriterLockWrapper.get_ReadLock() (source line information unavailable)
23. WindowsBase.dll#0!System.ComponentModel.PropertyChangedEventManager.OnPropertyChanged(sender=MyCompany.Windows.ViewModel.Window.WindowViewModel, args=System.ComponentModel.PropertyChangedEventArgs) (source line information unavailable)
24. MyCompany.Windows.Contracts.dll#0!MyCompany.Windows.ViewModel.Model.ViewModelItemBase.NotifyPropertyChanged(propertyName="IsActive") (source line information unavailable)
25. MyCompany.Windows.Contracts.dll#0!MyCompany.Windows.ViewModel.Window.WindowViewModel.set_IsActive(value=False) (source line information unavailable)
26. MyCompany.Windows.dll#0!MyCompany.Windows.ViewModel.Window.IsActiveBinding.OnWindowIsActiveChanged(sender=MyCompany.Xpf.Views.XpfRibbonShell.XpfRibbonShellView, e=System.EventArgs) (source line information unavailable)
27. WindowsBase.dll#0!MS.Internal.ComponentModel.PropertyChangeTracker.OnPropertyInvalidation(d=<N/A>, args=<N/A>) (source line information unavailable)
28. WindowsBase.dll#0!System.Windows.DependentList.InvalidateDependents(source=<N/A>, sourceArgs=<N/A>) (source line information unavailable)
29. WindowsBase.dll#0!System.Windows.DependencyObject.NotifyPropertyChange(args=<N/A>) (source line information unavailable)
30. WindowsBase.dll#0!System.Windows.DependencyObject.UpdateEffectiveValue(entryIndex=<N/A>, dp=<N/A>, metadata=<N/A>, oldEntry=<N/A>, newEntry=<N/A>, coerceWithDeferredReference=<N/A>, coerceWithCurrentValue=<N/A>, operationType=<N/A>) (source line information unavailable)
31. WindowsBase.dll#0!System.Windows.DependencyObject.SetValueCommon(dp=<N/A>, value=<N/A>, metadata=<N/A>, coerceWithDeferredReference=<N/A>, coerceWithCurrentValue=<N/A>, operationType=<N/A>, isInternal=<N/A>) (source line information unavailable)
32. WindowsBase.dll#0!System.Windows.DependencyObject.SetValue(key=<N/A>, value=<N/A>) (source line information unavailable)
33. PresentationFramework.dll#0!System.Windows.Window.HandleActivate(windowActivated=<N/A>) (source line information unavailable)
34. PresentationFramework.dll#0!System.Windows.Window.WmActivate(wParam=<N/A>) (source line information unavailable)
35. PresentationFramework.dll#0!System.Windows.Window.WindowFilterMessage(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
36. PresentationCore.dll#0!System.Windows.Interop.HwndSource.PublicHooksFilterMessage(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
37. WindowsBase.dll#0!MS.Win32.HwndWrapper.WndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
38. WindowsBase.dll#0!MS.Win32.HwndSubclass.DispatcherCallbackOperation(o=<N/A>) (source line information unavailable)
39. WindowsBase.dll#0!System.Windows.Threading.ExceptionWrapper.InternalRealCall(callback=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
40. WindowsBase.dll#0!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(source=System.Windows.Threading.Dispatcher, method=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<null>) (source line information unavailable)
41. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.WrappedInvoke(callback=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<N/A>) (source line information unavailable)
42. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.InvokeImpl(priority=<N/A>, timeout=<N/A>, method=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
43. WindowsBase.dll#0!MS.Win32.HwndSubclass.SubclassWndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>) (source line information unavailable)
[IL Method without Metadata]
44. WindowsBase.dll#0!MS.Internal.ReaderWriterLockWrapper.get_ReadLock() (source line information unavailable)
45. WindowsBase.dll#0!System.ComponentModel.PropertyChangedEventManager.OnPropertyChanged(sender=MyCompany.Windows.ViewModel.Window.WindowViewModel, args=System.ComponentModel.PropertyChangedEventArgs) (source line information unavailable)
46. MyCompany.Windows.Contracts.dll#0!MyCompany.Windows.ViewModel.Model.ViewModelItemBase.NotifyPropertyChanged(propertyName="IsActive") (source line information unavailable)
47. MyCompany.Windows.Contracts.dll#0!MyCompany.Windows.ViewModel.Window.WindowViewModel.set_IsActive(value=True) (source line information unavailable)
48. MyCompany.Windows.dll#0!MyCompany.Windows.ViewModel.Window.IsActiveBinding.OnWindowIsActiveChanged(sender=MyCompany.Xpf.Views.XpfRibbonShell.XpfRibbonShellView, e=System.EventArgs) (source line information unavailable)
49. WindowsBase.dll#0!MS.Internal.ComponentModel.PropertyChangeTracker.OnPropertyInvalidation(d=<N/A>, args=<N/A>) (source line information unavailable)
50. WindowsBase.dll#0!System.Windows.DependentList.InvalidateDependents(source=<N/A>, sourceArgs=<N/A>) (source line information unavailable)
51. WindowsBase.dll#0!System.Windows.DependencyObject.NotifyPropertyChange(args=<N/A>) (source line information unavailable)
52. WindowsBase.dll#0!System.Windows.DependencyObject.UpdateEffectiveValue(entryIndex=<N/A>, dp=<N/A>, metadata=<N/A>, oldEntry=<N/A>, newEntry=<N/A>, coerceWithDeferredReference=<N/A>, coerceWithCurrentValue=<N/A>, operationType=<N/A>) (source line information unavailable)
53. WindowsBase.dll#0!System.Windows.DependencyObject.SetValueCommon(dp=<N/A>, value=<N/A>, metadata=<N/A>, coerceWithDeferredReference=<N/A>, coerceWithCurrentValue=<N/A>, operationType=<N/A>, isInternal=<N/A>) (source line information unavailable)
54. WindowsBase.dll#0!System.Windows.DependencyObject.SetValue(key=<N/A>, value=<N/A>) (source line information unavailable)
55. PresentationFramework.dll#0!System.Windows.Window.HandleActivate(windowActivated=<N/A>) (source line information unavailable)
56. PresentationFramework.dll#0!System.Windows.Window.WmActivate(wParam=<N/A>) (source line information unavailable)
57. PresentationFramework.dll#0!System.Windows.Window.WindowFilterMessage(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
58. PresentationCore.dll#0!System.Windows.Interop.HwndSource.PublicHooksFilterMessage(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
59. WindowsBase.dll#0!MS.Win32.HwndWrapper.WndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
60. WindowsBase.dll#0!MS.Win32.HwndSubclass.DispatcherCallbackOperation(o=<N/A>) (source line information unavailable)
61. WindowsBase.dll#0!System.Windows.Threading.ExceptionWrapper.InternalRealCall(callback=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
62. WindowsBase.dll#0!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(source=System.Windows.Threading.Dispatcher, method=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<null>) (source line information unavailable)
63. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.WrappedInvoke(callback=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<N/A>) (source line information unavailable)
64. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.InvokeImpl(priority=<N/A>, timeout=<N/A>, method=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
65. WindowsBase.dll#0!MS.Win32.HwndSubclass.SubclassWndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>) (source line information unavailable)
[IL Method without Metadata]
66. WindowsBase.dll#0!System.ComponentModel.PropertyChangedEventManager.PrivateAddListener(source=<N/A>, listener=<N/A>, propertyName=<N/A>) (source line information unavailable)
67. PresentationFramework.dll#0!MS.Internal.Data.PropertyPathWorker.ReplaceItem(k=<N/A>, newO=<N/A>, parent=<N/A>) (source line information unavailable)
68. PresentationFramework.dll#0!MS.Internal.Data.PropertyPathWorker.UpdateSourceValueState(k=<N/A>, collectionView=<N/A>, newValue=<N/A>, isASubPropertyChange=<N/A>) (source line information unavailable)
69. PresentationFramework.dll#0!MS.Internal.Data.ClrBindingWorker.AttachDataItem() (source line information unavailable)
70. PresentationFramework.dll#0!System.Windows.Data.BindingExpression.Activate(item=<N/A>) (source line information unavailable)
71. PresentationFramework.dll#0!System.Windows.Data.BindingExpression.AttachToContext(attempt=<N/A>) (source line information unavailable)
72. PresentationFramework.dll#0!System.Windows.Data.BindingExpression.MS.Internal.Data.IDataBindEngineClient.AttachToContext(lastChance=<N/A>) (source line information unavailable)
73. PresentationFramework.dll#0!MS.Internal.Data.DataBindEngine+Task.Run(lastChance=<N/A>) (source line information unavailable)
74. PresentationFramework.dll#0!MS.Internal.Data.DataBindEngine.Run(arg=<N/A>) (source line information unavailable)
75. PresentationFramework.dll#0!MS.Internal.Data.DataBindEngine.OnLayoutUpdated(sender=<N/A>, e=<N/A>) (source line information unavailable)
76. PresentationCore.dll#0!System.Windows.ContextLayoutManager.fireLayoutUpdateEvent() (source line information unavailable)
77. PresentationCore.dll#0!System.Windows.ContextLayoutManager.UpdateLayout() (source line information unavailable)
78. PresentationCore.dll#0!System.Windows.ContextLayoutManager.UpdateLayoutCallback(arg=<N/A>) (source line information unavailable)
79. PresentationCore.dll#0!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks() (source line information unavailable)
80. PresentationCore.dll#0!System.Windows.Media.MediaContext.RenderMessageHandlerCore(resizedCompositionTarget=<N/A>) (source line information unavailable)
81. PresentationCore.dll#0!System.Windows.Media.MediaContext.RenderMessageHandler(resizedCompositionTarget=<N/A>) (source line information unavailable)
82. WindowsBase.dll#0!System.Windows.Threading.ExceptionWrapper.InternalRealCall(callback=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
83. WindowsBase.dll#0!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(source=System.Windows.Threading.Dispatcher, method=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<null>) (source line information unavailable)
84. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.WrappedInvoke(callback=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<N/A>) (source line information unavailable)
85. WindowsBase.dll#0!System.Windows.Threading.DispatcherOperation.InvokeImpl() (source line information unavailable)
86. mscorlib.dll#0!System.Threading.ExecutionContext.runTryCode(userData=<N/A>) (source line information unavailable)
87. mscorlib.dll#0!System.Threading.ExecutionContext.Run(executionContext=<N/A>, callback=<N/A>, state=<N/A>, ignoreSyncCtx=<N/A>) (source line information unavailable)
88. mscorlib.dll#0!System.Threading.ExecutionContext.Run(executionContext=<N/A>, callback=<N/A>, state=<N/A>) (source line information unavailable)
89. WindowsBase.dll#0!System.Windows.Threading.DispatcherOperation.Invoke() (source line information unavailable)
90. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.ProcessQueue() (source line information unavailable)
91. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.WndProcHook(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
92. WindowsBase.dll#0!MS.Win32.HwndWrapper.WndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
93. WindowsBase.dll#0!MS.Win32.HwndSubclass.DispatcherCallbackOperation(o=<N/A>) (source line information unavailable)
94. WindowsBase.dll#0!System.Windows.Threading.ExceptionWrapper.InternalRealCall(callback=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
95. WindowsBase.dll#0!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(source=System.Windows.Threading.Dispatcher, method=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<null>) (source line information unavailable)
96. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.WrappedInvoke(callback=<N/A>, args=<N/A>, numArgs=<N/A>, catchHandler=<N/A>) (source line information unavailable)
97. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.InvokeImpl(priority=<N/A>, timeout=<N/A>, method=<N/A>, args=<N/A>, numArgs=<N/A>) (source line information unavailable)
98. WindowsBase.dll#0!MS.Win32.HwndSubclass.SubclassWndProc(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>) (source line information unavailable)
[IL Method without Metadata]
[Internal Frame, 'M-->U']
[IL Method without Metadata]
99. WindowsBase.dll#0!System.Windows.Threading.Dispatcher.PushFrameImpl(frame=<N/A>) (source line information unavailable)
100. PresentationFramework.dll#0!System.Windows.Application.RunInternal(window=<N/A>) (source line information unavailable)
101. PresentationFramework.dll#0!System.Windows.Application.Run() (source line information unavailable)
102. MyProgram.exe#0!XamlGeneratedNamespace.GeneratedApplication.Main() (source line information unavailable)
似乎有人拿着
WriteLock
却从不释放-但是我如何检查谁拿着它呢?我已经粘贴了我到这里的整个stacktrace,是否有人要给我一些根本原因的建议,例如HwndSubclass
是什么,以及为什么随着IsActive
和WindowState
属性的更改,它在stacktrace中反复发生?如果您需要更多信息,请添加评论。
最佳答案
在研究了几乎所有相关组件的源代码之后,我终于解决了该问题。多亏了梦幻般的Reference Source网站,与ILSpy的反编译结果相比,源代码中的注释有很大帮助。
什么是PropertyChangedEventManager
?PropertyChangedEventManager
(source code here)是在并发环境中处理ProperyChanged
事件处理程序和通知的组件。换句话说,它是线程安全的。在内部,它使用ReaderWriterLock
保持线程安全。更改事件处理程序时将获取编写器锁,而当存在PropertyChanged
事件通知时将获取阅读器锁。PropertyChangedEventManager
通常由WPF控件使用。当我们将 View 模型附加/分离到控件时,我们将添加/删除PropertyChanged
事件处理程序。我一直想知道是谁持有写作者锁,它阻止了读者锁(get_ReadLock
),但实际上是GUI线程本身。
是的,这听起来很奇怪,但是它在PrivateAddListener
(source code here)内部,如stacktrace所示:
...
[IL Method without Metadata]
66. WindowsBase.dll#0!System.ComponentModel.PropertyChangedEventManager.PrivateAddListener(source=<N/A>, listener=<N/A>, propertyName=<N/A>) (source line information unavailable)
67. PresentationFramework.dll#0!MS.Internal.Data.PropertyPathWorker.ReplaceItem(k=<N/A>, newO=<N/A>, parent=<N/A>) (source line information unavailable)
...
顺便说一句,我总是被告知我们应该从后台线程在UI绑定(bind)的对象中触发
PropertyChanged
事件,但是自从.NET 4以来,情况并非如此。PropertyChangedEventManager
设计为可在并发环境中使用。排他(写入)锁仅在模型绑定(bind)到GUI控件时才使用,并且PropertyChanged
事件可以同时从多个后台线程触发。我们不需要手动将所有内容编码(marshal)至GUI线程。实际上,这是在后台线程中更新模型的非常重要的模式,有时这是唯一可接受的方法。请考虑以下情况:当我们有多个GUI/STA线程来提高应用程序的响应速度时。我们可以将同一实例绑定(bind)到不同GUI线程中的控件。更改模型后,我们根本无法将
PropertyChanged
通知编码到其中任何一个中。跨线程通知是不可避免的。什么是
SubclassWndProc
?HwndSubclass.SubclassWndProc
(source code here)是用于处理窗口消息的托管代码的入口点。它由 native 代码调用,因此我们始终可以在堆栈跟踪中找到[IL Method without Metadata]
和[Internal Frame, 'M-->U']
。奇怪的是,为什么在堆栈跟踪中会有几个
SubclassWndProc
调用?难道不应该分别一个接一个地处理窗口消息吗?要回答这个问题,我们需要检查方法的代码在stacktrace中重复出现:...
55. PresentationFramework.dll#0!System.Windows.Window.HandleActivate(windowActivated=<N/A>) (source line information unavailable)
56. PresentationFramework.dll#0!System.Windows.Window.WmActivate(wParam=<N/A>) (source line information unavailable)
57. PresentationFramework.dll#0!System.Windows.Window.WindowFilterMessage(hwnd=<N/A>, msg=<N/A>, wParam=<N/A>, lParam=<N/A>, handled=<N/A>) (source line information unavailable)
...
从源代码中,我注意到这些方法正在处理
WM_ACTIVATE
消息(好的,我们也可以从名称中分辨出来)。这是一条特殊消息as decribed in MSDN:Sent to both the window being activated and the window being deactivated. If the windows use the same input queue, the message is sent synchronously, first to the window procedure of the top-level window being deactivated, then to the window procedure of the top-level window being activated. If the windows use different input queues, the message is sent asynchronously, so the window is activated immediately.
由于该消息是在GUI线程中同步发送的,因此无需完成当前消息即可立即对其进行处理。这就是我们可以在堆栈跟踪中找到递归调用的原因。
它还解释了为什么
WindowViewMode.IsActive
设置了多次:#47中的
WindowViewModel.set_IsActive(value=True)
WindowViewModel.set_IsActive(value=False)
WindowViewModel.set_IsActive(value=True)
因为它将被“停用”并再次“激活”。
根本原因和解决方案
在
WindowViewModel
中,我们有一个IsActive
属性,该属性与Window.IsActive
属性同步。请注意,由于该属性是只读的,所以它不是双向绑定(bind)。激活窗口后,将设置WindowViewModel.IsActive
属性并触发PropertyChanged
事件。由于WPF控件已与 View 模型关联,因此将执行内部逻辑。我不清楚具体的逻辑(它是
[IL Method without Metadata]
),但是不幸的是,它会产生一条新的WM_ACTIVATE
消息。这一次又一次地发生,并最终停止了GUI线程。确保我们没有在绑定(bind)中使用
WindowViewModel.IsActive
后,将其更改为IsActive()
方法。我们不再需要触发PropertyChanged
事件,因为它不再是属性。我还发表了一条评论,说如果确实需要将
IsActive
作为属性,则需要确保PropertyChanged
事件在Dispatcher.BeginInvoke
内部触发,即使该事件已经在GUI线程中也是如此。我们需要确保下一个WM_ACTIVATE
消息是异步生成的。我无法解释的一件事
但是我仍然无法解释为什么当我们第三次或第四次获取阅读器锁时
ReaderWriterLock
会阻塞。我确实认为我们具有更深的递归PropertyChanged
通知,因此读取器锁的获取次数将比当前情况更多。但是,每次遇到此问题时,我们总是在堆栈跟踪中具有IsActive
属性。ReaderWriterLock
或WPF甚至在操作系统中是否有任何特殊保护?
关于c# - WPF应用程序在并发环境中被PropertyChangedEventManager挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24054940/