私たちはすべて我々はすべて知っているのWPFアプリケーションモデルは、単一のUIスレッドであることを知っています
しかし、実際には、我々は、バックグラウンドスレッドを実行できるDispatcher
UI要素を実行するために、
詳細は大きいブログで見つけることができます別のスレッドでWPFウィンドウを起動 、パート1:リードCopsey、ジュニア
このシナリオは、ようこそ画面はまだ非常にいいですロードするために使用されます
我々は次の例外発生したので、しかし、どうやらWPFチームは完全に、このようなシナリオを考えていなかった「コレクションが変更されました。」
System.Windows.Markup.XamlParseException: 集合已修改;可能无法执行枚举操作。 ---> System.InvalidOperationException: 集合已修改;可能无法执行枚举操作。
在 System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
在 System.Collections.Generic.List`1.Enumerator.MoveNextRare()
在 System.Collections.Generic.List`1.Enumerator.MoveNext()
在 System.Windows.Baml2006.WpfSharedBamlSchemaContext.GetKnownXamlType(Type type)
在 System.Windows.Baml2006.WpfSharedBamlSchemaContext.GetXamlType(Type type)
在 System.Windows.Baml2006.Baml2006SchemaContext.GetXamlType(Type type)
在 System.Xaml.XamlObjectWriter.GetXamlType(Type clrType)
在 System.Xaml.XamlObjectWriter.WriteEndMember()
在 System.Xaml.XamlWriter.WriteNode(XamlReader reader)
在 System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack`1 stack, IStyleConnector styleConnector)
在 System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
--- 内部异常堆栈跟踪的结尾 ---
在 System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
在 System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
在 System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
在 System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
我々はまた、別のパスをキャッチしますが、質問の最後のポイントは、上記スタック質問GetKnownXamlType
私たちは、ソースコードを見て
のみforeachループ全体のプロセスは、その後明らかにこれは、マルチスレッドの問題です
_themeHelper
唯一のバッキングフィールドとして割り当て、そしてThemeKnownTypeHelpers
それは二つの可能性が存在する必要がありますので、プロパティは、スレッドセーフではありません。
1、他のスレッドのプロパティの割り当てのサイクル時間
2,2スレッドが同時にアクセスするThemeKnownTypeHelpers
プロパティを
私たちのトラッキングコードは、Application.LoadComponent
静的メソッドを呼び出しますXamlReader.LoadBaml
この方法では、読者が作成されますBAML
私たちは、その下のパラメータを懸念しているBaml2006SchemaContext
、このクラスのコンストラクタは公に静的プロパティで使用されていますXamlReader.BamlSharedSchemaContext
。これは、スレッドセーフであるLazy
構造WpfSharedBamlSchemaContext
。
そのため、すべてのスレッドが同じにアクセスWpfSharedBamlSchemaContext
我々は見てThemeKnownTypeHelper
、それは抽象クラスであり、PresentationFramework
そのサブクラスを見つけられませんでした
が、我々はPresentationFramework.Luna
内部を見つけKnownTypeHelper
、それが訪問したThemeKnownTypeHelpers
財産を、そして、コレクションが変更されました
そして、このクラスはどこにそれが使用されるのですか?我々はSystemResource.LoadExternalAssembly
それを発見しました。
这里是wpf的主题设置代码,当我们需要引入外部主题时,会从同名不同后缀的程序集进行查找。具体可参见
默认的WPF样式在哪里_黄腾霄的博客-CSDN博客
不过在加载主题时是会适用锁的就是ThermeDictionaryLock
,这个锁会对主题的Style
和Template
以及Application
的资源字典加锁。但是调试发现,在WpfXamlLoader.TransformNodes
的多条路径(比如下面这条)都可以无锁访问到GetKnownXamlType
方法,而后者方法内的锁与前者不同。
所以也希望这个bug可以被修复
参考链接:
- Launching a WPF Window in a Separate Thread, Part 1 : Reed Copsey, Jr.
- WpfSharedBamlSchemaContext
- XamlReader
- Baml2006SchemaContext
- KnownTypeHelper
- ThemeKnownTypeHelper
- SystemResource
- 默认的WPF样式在哪里 - huangtengxiao
- 默认的WPF样式在哪里_黄腾霄的博客-CSDN博客
本文会经常更新,请阅读个人博客原文: https://xinyuehtx.github.io/ ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。