简体   繁体   English

如何在WinForms中测试[STAThread]对性能的影响?

[英]How do I test performance impact of [STAThread] in WinForms?

I managed to write a relatively large WinForms application in C# that functions correctly without the [STAThread] attribute on the Main() method. 我设法用C#编写了一个相对较大的WinForms应用程序,该应用程序在Main()方法上没有[STAThread]属性的情况下可以正常运行。

To accomplish this I had to override a lot of WinForms functionality (such as using a custom BeginInvoke and Invoke functions), use a custom message loop instead of Application.Run , use a custom file dialog instead of OpenFileDialog and SaveFileDialog , and use WM_DROPFILES for drag-and-drop instead of the WinForms out-of-the-box OLE approach. 为此,我必须重写许多WinForms功能(例如使用自定义的BeginInvokeInvoke函数),使用自定义消息循环代替Application.Run ,使用自定义文件对话框代替OpenFileDialogSaveFileDialog ,并使用WM_DROPFILES拖放而不是WinForms开箱即用的OLE方法。 This is all "for science". 这都是“为了科学”。

Now I want to test for any possible performance impact of omitting the STAThreadAttribute from all GUI threads. 现在,我想测试从所有GUI线程中删除STAThreadAttribute可能对性能产生的影响。 I do not possess deep enough knowledge of the COM configuration used internally by the Control class to be able to predict such impact. 我对Control类内部使用的COM配置没有足够的了解,无法预测这种影响。 Speed of execution probably depends on which thread is calling the internal COM object of the Control . 执行速度可能取决于哪个线程正在调用Control的内部COM对象。

Admittedly, I am having trouble coming up with a benchmark that would test for performance impact pertaining to [STAThread] , because I am unsure of which functions/operations would be affected by such a change (specifically related to the Control class). 诚然,我很难找到一个基准来测试与[STAThread]有关的性能影响,因为我不确定哪些功能/操作会受到这种更改的影响(特别是与Control类有关)。

What should I look for exactly? 我到底应该找什么? Which operations/methods from the Control class should I expect to run faster/slower by omitting [STAThread] , if any? 我希望通过省略[STAThread] (如果有)来期望Control类中的哪些操作/方法运行更快/更慢?

Addendum: The rationale is that I am slowly migrating my application to use a custom windowing system (for portability reasons, mainly for using Mono on Linux, whose WinForms implementation is not complete), so I had to override a lot of functionality myself anyway. 附录:理由是我正在慢慢地将我的应用程序迁移为使用自定义窗口系统(出于可移植性的原因,主要是因为在Linux上使用Mono,其WinForms实现尚未完成),因此无论如何我都不得不重写许多功能。 It was merely a coincidence that I noticed that I had overridden so much functionality that I could omit [STAThread] and everything would still work as expected. 只是巧合,我注意到我已经重写了太多的功能,可以忽略[STAThread],并且一切仍然可以按预期进行。

I expect a change in performance due to COM marshaling calls from the ThreadPool (which is configured as MTA), and the GUI thread (which by default should be configured as STA). 由于来自ThreadPool (配置为MTA)和GUI线程(默认情况下应配置为STA)的COM封送处理调用,我预计性能会有所变化。 Calls from the ThreadPool to the GUI thread would need to be marshalled due to them being configured in different thread apartments, which introduces a synchronization overhead. 由于在不同线程单元中配置了从ThreadPool到GUI线程的调用,因此需要将它们编组,这会导致同步开销。 By leaving the GUI thread as MTA, the marshalling should be reduced, hence possibly faster execution of function calls. 通过将GUI线程保留为MTA,应减少编组,从而可能更快地执行函数调用。 I would like to test this claim pragmatically. 我想以务实的方式对此要求进行检验。

[STAThread] has too much mystique attached. [STAThread]具有过多的神秘感。 But in practice is very simple, you make a promise . 但是实际上很简单,您就可以保证 Cross your heart, hope to die. 跨过你的心,希望死。 You promise to the OS that your main thread is a hospitable home for code that is not thread-safe. 您向操作系统保证,主线程是线程安全的好地方。 Keeping the promise requires having a dispatcher (Application.Run) and never blocking the thread. 遵守承诺要求拥有调度程序(Application.Run),并且永远不要阻塞线程。 Things you do later, that's why you have to make the promise up front. 您以后要做的事情,这就是为什么您必须提前做出承诺的原因。

In a GUI app that runs on Windows there is always a lot of that code around. 在Windows上运行的GUI应用程序中,总是会有很多这样的代码。 Whatever framework you use is the more obvious place for such code. 无论使用哪种框架,此类代码的位置都更加明显。 But the far nastier stuff is the code you cannot see. 但是,最糟糕的是您看不到的代码。 The kind that lives inside shell extensions, in UI Automation code, in apps that want to provide data through the clipboard or drag+drop, in hooks installed with SetWindowsHookEx, in screen-readers for users with visual impairments, in ActiveX components that expect PostMessage to work. 在外壳扩展,UI自动化代码中,希望通过剪贴板或拖放操作提供数据的应用中,SetWindowsHookEx附带的挂钩中,视力不佳的用户的屏幕阅读器中,期望PostMessage的ActiveX组件中都存在这种类型上班。 Such code doesn't have to be thread-safe, the OS does not demand that it is, mostly because whomever wrote that code has no way to test it. 这样的代码不必一定是线程安全的,OS并不需要如此,主要是因为编写该代码的人无法对其进行测试。 He didn't know beans about your app. 他不了解您的应用程序。

It matters to the OS because it has to do something when such code does not run on the UI thread. 这对OS很重要,因为当此类代码在UI线程上运行时,它必须执行某些操作。 Since the code explicitly announces that it is not thread-safe, or doesn't have to be, it has to keep the code safe anyway. 由于代码明确声明它不是线程安全的或不是必须的,因此无论如何都必须确保代码安全。 Only way that is possible is by initializing the code and make any future calls to it on the same thread. 唯一可行的方法是初始化代码,并在同一线程上对其进行任何将来的调用。 That requires some trickery, the code has to be marshalled , made to run on another thread, having a dispatcher loop is crucial to make that work. 这需要一些技巧,代码必须经过编组 ,使其可以在另一个线程上运行,而调度程序循环对于完成此工作至关重要。 A dispatcher is the universal solution to the producer-consumer problem . 调度员是生产者-消费者问题的通用解决方案。

But if the code is initialized from the right thread then this is not necessary. 但是,如果代码是从正确的线程初始化的,则没有必要。 How does the OS know whether it is the "right thread"? 操作系统如何知道它是否是“正确的线程”? The [STAThread] promise tells it that. [STAThread]承诺告诉了这一点。

So first conclusion you can make is not marking the UI thread as STA actually makes your program slower . 因此,您可以得出的第一个结论是不将UI线程标记为STA,因为它实际上会使您的程序变慢 Since every call is marshalled. 由于每个电话都被编组。 Not the only problem, there is a lot of such code that cannot be marshalled. 这不是唯一的问题,有很多这样的代码无法编组。 The author has to do extra work to enable it, he has to provide a proxy/stub. 作者必须做额外的工作才能启用它,他必须提供代理/存根。 Sometimes it is just too hard, often he simply doesn't since he relies on you doing it right. 有时这太难了,但他却根本不这样做,因为他依靠您来正确地做。 So the call will fail miserably. 因此,通话将失败。 But this happens in external code, you won't find out. 但这是在外部代码中发生的,您不会发现。 So stuff just doesn't work. 所以东西就是行不通的。 Or deadlocks. 或僵局。 Or an expected event doesn't get raised. 否则不会引发预期的事件。 Miserable stuff. 惨的东西。 You must use [STAThread] to avoid the misery. 必须使用[STAThread]避免痛苦。

This is otherwise a pure Windows implementation detail, it does not exist on the Unixes. 否则,这是纯Windows实现的详细信息,在Unixes上不存在。 They have just completely different ways to provide these features, if at all. 他们提供这些功能的方式完全不同(如果有的话)。 So [STAThread] does not mean anything on such an OS and testing what happens without it doesn't tell you anything. 因此,[STAThread]在这样的操作系统上并不意味着任何事情,并且在没有它的情况下进行测试不会告诉您任何事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM