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

时间:2017-10-03 10:50:26

标签: c# .net winforms performance com

我设法在C#中编写了一个相对较大的WinForms应用程序,它在[STAThread]方法上没有Main()属性的情况下正常运行。

要实现这一点,我必须覆盖很多WinForms功能(例如使用自定义BeginInvokeInvoke函数),使用自定义消息循环而不是Application.Run,使用自定义文件对话框而不是OpenFileDialogSaveFileDialog,并使用WM_DROPFILES进行拖放而不是WinForms开箱即用的OLE方法。这完全是为了“科学”。

现在我想测试从所有GUI线程中省略STAThreadAttribute可能对性能产生的影响。我对Control类内部使用的COM配置知之甚少,无法预测此类影响。执行速度可能取决于哪个线程正在调用Control的内部COM对象。

不可否认,我无法提出可以测试与[STAThread]相关的性能影响的基准测试,因为我不确定哪些功能/操作会受到这种更改的影响(特别是与{相关) {1}}类)。

我应该准确找到什么?我应该通过省略Control来更快/更慢地运行Control类中的哪些操作/方法?

附录:理由是我正在慢慢迁移我的应用程序以使用自定义窗口系统(出于可移植性的原因,主要是因为在Linux上使用Mono,其WinForms实现不完整),所以我不管怎么说,我自己必须覆盖很多功能。我只是巧合地注意到我已经覆盖了如此多的功能,我可以省略[STAThread],一切都会按预期工作。

我期望由于来自[STAThread](配置为MTA)的COM编组调用以及GUI线程(默认情况下应配置为STA)导致性能发生变化。需要对ThreadPool到GUI线程的调用进行编组,因为它们在不同的线程公寓中配置,这会引入同步开销。通过将GUI线程保留为MTA,应该减少编组,因此可能更快地执行函数调用。我想以务实的方式测试这个说法。

1 个答案:

答案 0 :(得分:7)

[STAThread]附有太多神秘感。但实际上很简单,你做了一个承诺。穿过你的心,希望死去。您向操作系统承诺,您的主线程是一个热情好客的主页,用于非线程安全的代码。保持承诺需要有一个调度程序(Application.Run)并且永远不会阻塞该线程。你以后做的事情,这就是你必须预先做出承诺的原因。

在Windows上运行的GUI应用程序中,总会有很多代码。无论您使用什么框架,这些代码的位置都更明显。但是更糟糕的东西是你看不到的代码。在UI自动化代码中,在希望通过剪贴板提供数据的应用程序中,或者在使用SetWindowsHookEx安装的挂钩中拖放,在具有视觉障碍的用户的屏幕阅读器中,在期望PostMessage的ActiveX组件中的类型上班。这样的代码没有是线程安全的,操作系统不要求它是,主要是因为谁写了代码无法测试它。他不知道关于你的应用程序的bean。

对操作系统很重要,因为当这些代码在UI线程上运行时,它必须执行某些操作。由于代码明确宣布它不是线程安全的,或者不必是,它必须保持代码安全。唯一可能的方法是初始化代码并在同一个线程上进行任何将来的调用。这需要一些技巧,代码必须 marshalled ,使其在另一个线程上运行,具有调度程序循环对于使其工作至关重要。调度程序是producer-consumer problem的通用解决方案。

但是如果从正确的线程初始化代码,那么这不是必需的。操作系统如何知道它是否是“正确的线程”? [STAThread]承诺告诉它。

因此,您可以做出的第一个结论是不标记UI线程,因为STA实际上使您的程序更慢。因为每次通话都是编组的。不是唯一的问题,有很多这样的代码无法编组。作者必须做额外的工作来启用它,他必须提供代理/存根。有时它太难了,往往他根本就没有,因为他依靠你做得对。所以电话会失败。但这种情况发生在外部代码中,你不会发现。所以东西不起作用。或死锁。或者预期的事件不会被提升。悲惨的东西。你必须使用[STAThread]来避免痛苦。

这是一个纯粹的Windows实现细节,它在Unix上不存在。如果有的话,他们提供这些功能的方式完全不同。所以[STAThread]并不意味着在这样的操作系统上有任何意义,如果没有它就会测试会发生什么事情。