【问题标题】:How do I test performance impact of [STAThread] in WinForms?如何在 WinForms 中测试 [STAThread] 的性能影响?
【发布时间】:2018-03-14 13:07:31
【问题描述】:

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

为此,我必须重写许多 WinForms 功能(例如使用自定义 BeginInvokeInvoke 函数),使用自定义消息循环而不是 Application.Run,使用自定义文件对话框而不是 @ 987654330@ 和 SaveFileDialog,并使用 WM_DROPFILES 进行拖放,而不是 WinForms 开箱即用的 OLE 方法。这都是“为了科学”。

现在我想测试在所有 GUI 线程中省略 STAThreadAttribute 可能对性能造成的影响。我对Control 类内部使用的COM 配置的了解不够深入,无法预测这种影响。执行速度可能取决于哪个线程正在调用Control 的内部 COM 对象。

诚然,我无法提出一个测试与[STAThread] 相关的性能影响的基准,因为我不确定哪些功能/操作会受到这种更改的影响(特别是与Control类)。

我究竟应该寻找什么?通过省略[STAThread](如果有),我应该期望Control 类中的哪些操作/方法运行得更快/更慢?

附录:理由是我正在慢慢迁移我的应用程序以使用自定义窗口系统(出于便携性原因,主要是在 Linux 上使用 Mono,其 WinForms 实现不完整),所以我无论如何,我不得不自己重写很多功能。只是一个巧合,我注意到我已经覆盖了很多功能,以至于我可以省略 [STAThread] 并且一切仍将按预期工作。

由于来自ThreadPool(配置为 MTA)和 GUI 线程(默认情况下应配置为 STA)的 COM 编组调用,我预计性能会发生变化。从 ThreadPool 到 GUI 线程的调用需要编组,因为它们被配置在不同的线程单元中,这会引入同步开销。通过将 GUI 线程保留为 MTA,应该减少编组,因此可能更快地执行函数调用。我想实际地测试一下这个说法。

【问题讨论】:

  • 基于此; stackoverflow.com/questions/1361033/what-does-stathread-do,不建议删除此属性,因为 WinForms 不保证正确执行。删除属性的原因是什么?
  • 您为什么预计性能会发生(可衡量的)变化?
  • 我更新了问题以回答两个 cmets。
  • STA/MTA 仅适用于 COM(此处必须是 UI 控件)。我相信任何 UI 控件都只能在 STA(通常是主 STA)中运行。如果您以某种方式设法让您的主 UI 线程进入 MTA,则必须编组从 MTA 到控件 STA 的所有调用。我什至不确定这将如何工作。您必须通过代理调用的 COM 指针进行调用。直接通过普通指针调用似乎可以工作,因为一切都在进行中。但是,您不再遵守规则,并且有一天您的代码很可能会因为竞态条件而失败。

标签: c# .net winforms performance com


【解决方案1】:

[STAThread] 有太多的神秘感。但实际上很简单,你做出一个promise。穿过你的心,希望死去。您向操作系统保证,您的主线程是非线程安全代码的好客之家。遵守承诺需要有一个调度程序(Application.Run)并且永远不会阻塞线程。你以后做的事情,这就是为什么你必须事先做出承诺。

在 Windows 上运行的 GUI 应用程序中,总是有很多这样的代码。无论您使用什么框架,这些代码都是更明显的地方。但更糟糕的是你看不到的代码。存在于 shell 扩展中、UI 自动化代码中、希望通过剪贴板或拖放提供数据的应用程序中、使用 SetWindowsHookEx 安装的挂钩中、为有视觉障碍的用户提供的屏幕阅读器中、期望 PostMessage 的 ActiveX 组件中的那种去工作。这样的代码必须是线程安全的,操作系统并不要求它是线程安全的,主要是因为编写该代码的人无法测试它。他对你的应用一无所知。

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

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

因此,您可以得出的第一个结论是没有将 UI 线程标记为 STA 实际上会使您的程序变慢。因为每个呼叫都是编组的。不是唯一的问题,有很多这样的代码无法编组。作者必须做额外的工作才能启用它,他必须提供代理/存根。有时这太难了,他通常根本不这样做,因为他依赖于你做对了。所以调用会惨遭失败。但这发生在外部代码中,你不会发现。所以东西就是行不通。或者死锁。或者没有引发预期的事件。可怜的东西。您必须使用 [STAThread] 来避免痛苦。

否则这是一个纯粹的 Windows 实现细节,它在 Unix 上不存在。他们有完全不同的方式来提供这些功能,如果有的话。因此,[STAThread] 在这样的操作系统上没有任何意义,并且在没有它的情况下测试会发生什么并不能告诉您任何事情。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-29
    • 2020-02-12
    • 2014-08-11
    • 1970-01-01
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多