当我尝试在WinForm中启动OpenFileDialog
时,我最近在程序中遇到了与STA相关的错误。我已经做了一些阅读,在我将[STAThread]
属性添加到主线程之前,我想知道它将如何影响我的程序执行。
我是COM的外国人,所以我读到的所有内容都不适合我。与我相关的一些观点是:
[STAThread]属性将应用程序定义为使用单线程单元模型。 更具体地说,它将应用程序线程的状态更改为单线程。 http://www.a2zdotnet.com/View.aspx?Id=93
当许多线程访问对象时,STA体系结构会对性能造成重大损失。每个线程对该对象的访问都是序列化的,因此每个线程必须排队等待轮到该对象。
http://www.codeproject.com/Articles/9190/Understanding-The-COM-Single-Threaded-Apartment-Pa
我理解线程安全的必要性,但我仍然不理解 STAThread的作用。在我的程序(我从另一个开发人员继承)中,主线程启动了几个其他线程,其中一个初始化UI表单 - 我认为这是问题出现的地方。添加[STAThread]
新线程会发生什么?这是否会影响非Windows对象的多线程通信?
当我尝试在其中一个表单中打开OpenFileDialog
时发生错误。我使用VS设计器将对话框添加到表单中:它不起作用。然后我尝试在全局文件中创建一个对话框,该文件由主线程运行并从我的表单中调用该实例。它没有效果。
答案 0 :(得分:6)
[STAThread]或Thread.SetApartmentState()是一个非常非常重要的事情。您对编写行为良好的代码的操作系统进行承诺。 Windows中的大量代码以及您使用的非线程安全的组件都很重要。此类代码的标准示例包括Clipboard,Drag + Drop,shell对话框(如OpenFileDialog),WebBrowser等组件以及.NET类包装的许多Windows子组件。
线程安全始终是一件大事,编写真正的线程安全代码非常非常困难。 .NET Framework本身很少完成它。非常基本的类列表List<>不是线程安全的。
通过使 promise 表现良好,您必须遵守在报告自己为STA线程的线程中编写代码的规则。你必须做两件基本的事情:
您必须提取消息循环。在Winforms或WPF应用程序中的Aka Application.Run()。消息循环是一种基本机制,通过它您可以获取在特定线程上运行的代码。它是producer-consumer problem的通用解决方案。这解决了线程安全问题,如果你总是从同一个线程调用线程不安全的代码,那么它就不再是不安全了。
你绝不能阻止你的线程。阻止STA线程非常可能导致死锁。因为它可以阻止那些不是线程安全的代码块被调用。在CLR中有对此的核心支持,使用WaitOne()阻塞STA线程会导致它自己抽取消息循环。
在Winforms或WPF应用程序中可以轻松满足这些要求。它们是完全旨在帮助您实现它们的类库。关于他们行为方式的几乎所有方面都受其影响。
您必须将GUI应用中的Main()方法标记为[STAThread]。创建窗户时需要坚如磐石的要求。
支持并且可以创建显示窗口的另一个线程。这次你必须调用SetApartmentState()来切换到STA,它不能是线程池线程。如果使用某种控件,在Winforms中你会被SystemEvents类严重咬伤,这一点非常困难。它有一个开始在错误的线程上提升事件的诀窍。调试这样的问题需要look like this的黑带技能。那可能会吓到你。