如何在 WinForms 中测试 [STAThread] 的性能影响?
How do I test performance impact of [STAThread] in WinForms?
我设法用 C# 编写了一个相对较大的 WinForms 应用程序,它在 Main()
方法上没有 [STAThread]
属性的情况下也能正常运行。
为了实现这一点,我不得不重写许多 WinForms 功能(例如使用自定义 BeginInvoke
和 Invoke
函数),使用自定义消息循环而不是 Application.Run
,使用自定义文件对话框而不是 OpenFileDialog
和 SaveFileDialog
,并使用 WM_DROPFILES 进行拖放而不是 WinForms 开箱即用的 OLE 方法。这就是全部 "for science".
现在我想测试省略 STAThreadAttribute from all GUI threads. I do not possess deep enough knowledge of the COM configuration used internally by the Control class 的任何可能的性能影响,以便能够预测这种影响。执行速度可能取决于哪个线程正在调用 Control
.
的内部 COM 对象
无可否认,我无法想出一个基准来测试与 [STAThread]
有关的性能影响,因为我不确定哪个 functions/operations 会受到这种变化的影响(特别是与 Control
class).
有关
我究竟应该寻找什么? Control
class 中的哪个 operations/methods 我应该通过省略 [STAThread]
来 运行 faster/slower,如果有的话?
附录: 理由是我正在慢慢迁移我的应用程序以使用自定义窗口系统(出于可移植性原因,主要是为了在 Linux 上使用 Mono,其WinForms 实现不完整),所以我不得不自己重写很多功能。只是巧合,我注意到我已经覆盖了太多的功能,以至于我可以省略 [STAThread] 而一切仍会按预期工作。
我预计来自 ThreadPool
(配置为 MTA)和 GUI 线程(默认情况下应配置为 STA)的 COM 编组调用会导致性能发生变化。从 ThreadPool
到 GUI 线程的调用需要编组,因为它们是在不同的线程单元中配置的,这会引入同步开销。通过将 GUI 线程保留为 MTA,应该减少编组,因此可能更快地执行函数调用。我想务实地检验这个说法。
[STAThread] 有太多的神秘色彩。但实际上很简单,你做出一个承诺。划过心,望死。您向 OS 保证您的主线程是非线程安全代码的好客之家。信守承诺需要有一个调度程序 (Application.Run) 并且从不阻塞线程。以后做的事,所以要提前承诺。
在 运行 上 Windows 的 GUI 应用程序中,总是有很多这样的代码。无论您使用什么框架,此类代码都比较明显。但更糟糕的是你看不到的代码。存在于 shell 扩展中的那种,在 UI 自动化代码中,在想要通过剪贴板或拖放提供数据的应用程序中,在使用 SetWindowsHookEx 安装的钩子中,在屏幕中- 在希望 PostMessage 工作的 ActiveX 组件中为有视觉障碍的用户提供阅读器。这样的代码 没有 是线程安全的, OS 不要求它是线程安全的,主要是因为编写该代码的人无法测试它。他对你的应用一无所知。
它对 OS 很重要,因为当这样的代码在 UI 线程上 而不是 运行 时,它必须做一些事情。由于代码明确声明它不是线程安全的,或者不一定是线程安全的,因此它无论如何都必须保持代码安全。唯一可行的方法是初始化代码并在同一线程上对其进行任何未来调用。这需要一些技巧,代码必须 编组 ,在另一个线程上 运行,拥有一个调度程序循环对于完成这项工作至关重要。调度程序是 producer-consumer problem.
的通用解决方案
但是如果代码是从正确的线程初始化的,那么就没有必要了。 OS 如何知道它是否是 "right thread"? [STAThread] 承诺告诉它。
因此,您可以得出的第一个结论是没有将 UI 线程标记为 STA 实际上会使您的程序 变慢 。因为每个电话都是编组的。不是唯一的问题,还有很多这样的代码无法编组。作者必须做额外的工作才能启用它,他必须提供一个 proxy/stub。有时这太难了,通常他根本不这样做,因为他依靠你做对了。所以这个电话会惨败。但这发生在外部代码中,您不会发现。所以东西就是行不通。或者死锁。或者没有引发预期的事件。悲惨的东西。您必须使用[STAThread]来避免痛苦。
否则这是一个纯粹的 Windows 实现细节,它在 Unix 上不存在。他们提供这些功能的方式完全不同,如果有的话。所以 [STAThread] 在这样的 OS 上没有任何意义,测试没有它会发生什么也不会告诉你任何事情。
我设法用 C# 编写了一个相对较大的 WinForms 应用程序,它在 Main()
方法上没有 [STAThread]
属性的情况下也能正常运行。
为了实现这一点,我不得不重写许多 WinForms 功能(例如使用自定义 BeginInvoke
和 Invoke
函数),使用自定义消息循环而不是 Application.Run
,使用自定义文件对话框而不是 OpenFileDialog
和 SaveFileDialog
,并使用 WM_DROPFILES 进行拖放而不是 WinForms 开箱即用的 OLE 方法。这就是全部 "for science".
现在我想测试省略 STAThreadAttribute from all GUI threads. I do not possess deep enough knowledge of the COM configuration used internally by the Control class 的任何可能的性能影响,以便能够预测这种影响。执行速度可能取决于哪个线程正在调用 Control
.
无可否认,我无法想出一个基准来测试与 [STAThread]
有关的性能影响,因为我不确定哪个 functions/operations 会受到这种变化的影响(特别是与 Control
class).
我究竟应该寻找什么? Control
class 中的哪个 operations/methods 我应该通过省略 [STAThread]
来 运行 faster/slower,如果有的话?
附录: 理由是我正在慢慢迁移我的应用程序以使用自定义窗口系统(出于可移植性原因,主要是为了在 Linux 上使用 Mono,其WinForms 实现不完整),所以我不得不自己重写很多功能。只是巧合,我注意到我已经覆盖了太多的功能,以至于我可以省略 [STAThread] 而一切仍会按预期工作。
我预计来自 ThreadPool
(配置为 MTA)和 GUI 线程(默认情况下应配置为 STA)的 COM 编组调用会导致性能发生变化。从 ThreadPool
到 GUI 线程的调用需要编组,因为它们是在不同的线程单元中配置的,这会引入同步开销。通过将 GUI 线程保留为 MTA,应该减少编组,因此可能更快地执行函数调用。我想务实地检验这个说法。
[STAThread] 有太多的神秘色彩。但实际上很简单,你做出一个承诺。划过心,望死。您向 OS 保证您的主线程是非线程安全代码的好客之家。信守承诺需要有一个调度程序 (Application.Run) 并且从不阻塞线程。以后做的事,所以要提前承诺。
在 运行 上 Windows 的 GUI 应用程序中,总是有很多这样的代码。无论您使用什么框架,此类代码都比较明显。但更糟糕的是你看不到的代码。存在于 shell 扩展中的那种,在 UI 自动化代码中,在想要通过剪贴板或拖放提供数据的应用程序中,在使用 SetWindowsHookEx 安装的钩子中,在屏幕中- 在希望 PostMessage 工作的 ActiveX 组件中为有视觉障碍的用户提供阅读器。这样的代码 没有 是线程安全的, OS 不要求它是线程安全的,主要是因为编写该代码的人无法测试它。他对你的应用一无所知。
它对 OS 很重要,因为当这样的代码在 UI 线程上 而不是 运行 时,它必须做一些事情。由于代码明确声明它不是线程安全的,或者不一定是线程安全的,因此它无论如何都必须保持代码安全。唯一可行的方法是初始化代码并在同一线程上对其进行任何未来调用。这需要一些技巧,代码必须 编组 ,在另一个线程上 运行,拥有一个调度程序循环对于完成这项工作至关重要。调度程序是 producer-consumer problem.
的通用解决方案但是如果代码是从正确的线程初始化的,那么就没有必要了。 OS 如何知道它是否是 "right thread"? [STAThread] 承诺告诉它。
因此,您可以得出的第一个结论是没有将 UI 线程标记为 STA 实际上会使您的程序 变慢 。因为每个电话都是编组的。不是唯一的问题,还有很多这样的代码无法编组。作者必须做额外的工作才能启用它,他必须提供一个 proxy/stub。有时这太难了,通常他根本不这样做,因为他依靠你做对了。所以这个电话会惨败。但这发生在外部代码中,您不会发现。所以东西就是行不通。或者死锁。或者没有引发预期的事件。悲惨的东西。您必须使用[STAThread]来避免痛苦。
否则这是一个纯粹的 Windows 实现细节,它在 Unix 上不存在。他们提供这些功能的方式完全不同,如果有的话。所以 [STAThread] 在这样的 OS 上没有任何意义,测试没有它会发生什么也不会告诉你任何事情。