【问题标题】:Why can't we change apartment state of a ThreadPool thread and why don't we require a messagepump when ShowDialog is used?为什么我们不能更改 ThreadPool 线程的单元状态,为什么我们在使用 ShowDialog 时不需要消息泵?
【发布时间】:2009-08-15 00:54:38
【问题描述】:

最近,我遇到了这种情况,我想在另一个线程(不是主/UI 线程)上显示一个表单。我使用了线程池线程。该表单承载了一个 RCW(用于 COM 组件)。实例化表单给了我一个例外,即线程必须是 STA。我试图将公寓状态设置为 STA。但是,这也不起作用。我终于明确地创建了一个线程并且工作正常(我使用了 ShowDialog 并且不需要创建消息泵)。

编辑:

  • 我知道线程池线程是 MTA。但是,为什么不能设置为 STA? 只是好奇这个。

  • 我脑海中突然出现的另一个问题:为什么我们在使用 Showdialog()(显示表单)时不需要消息泵

【问题讨论】:

    标签: c# winforms multithreading


    【解决方案1】:

    通过调用 CoInitializeEx() 选择公寓。线程池中的线程已经进行了该调用,因此无法在该调用后更改单元。

    线程池选择 MTA 是有道理的,毕竟它是作为工作线程使用的,不应该被需要编组的方法调用阻塞。选择单线程单元还有一个额外的要求,那就是泵送一个消息循环。您永远不会期望线程池线程会做的事情。

    消息循环是必要的,因为这是 COM 用来编组在另一个线程上进行的调用的工具。该调用必须在 STA 线程中“注入”,这只有在线程处于已知静止状态时才有可能。如果不是,这样的调用将导致重大的重入问题。即使线程正在循环循环,它有时也会这样做。

    您不需要自己使用 Application.Run() 泵送消息循环,因为 ShowDialog() 会启动它自己的消息循环。这就是它获得模态的方式。对话框一关闭,嵌套循环就会退出。

    【讨论】:

    • 感谢您的回答。你能指向一个链接,它说 ShowDialog() 启动它自己的消息循环。
    【解决方案2】:

    您不应该依赖线程池线程的特定行为。一般来说,线程池中的线程应该能够在您不知情的情况下随时被 CLR 替换。线程池线程旨在用于简单任务,最好是短期任务。

    如果您想对线程设置进行细粒度控制,您应该创建一个专用线程。设置公寓状态就是一个很好的例子。


    除了上述理论上的原因之外,您的尝试还有一个实际问题。在第二个线程上托管表单不起作用(没有大量额外工作)。表单必须与消息泵在同一线程上运行 - 否则,它们将不会收到任何 Windows 消息,并且不会正确更新。

    如果您为该线程实现完整的消息泵,则可以在单独的线程上创建一个表单,但通常最好将您的工作项放在后台线程上,并使用异步编程技术来保留您的自 UI 线程反应灵敏。

    【讨论】:

    • 感谢您的回答。 “线程池中的一个线程应该可以在你不知情的情况下随时被 CLR 替换。”不知道这个。究竟是什么意思?
    • 线程池在程序运行时创建和销毁线程。线程池中没有(必然)只有固定数量的线程。 .NET 4 的线程池在这方面更加灵活。它有各种用于窃取工作等的新工具,这会导致它在应用运行时产生和销毁线程。
    • 哦..是的,我完全了解消息泵。因此,通过线程池线程或新线程创建表单将需要将消息泵附加到该线程。但是,我使用 ShowDialog() 来显示表单并且不需要消息泵,因为它是一个阻塞调用。实际上,我只是好奇为什么我们不能将 ThreadPool 线程的单元状态更改为 STA。谢谢。
    • ShowDialog 仍然需要消息泵。如果你没有这样做,你会得到一些奇怪的行为,比如如果其他窗口移动到顶部则不会重绘(有时)......它通常可以工作,因为它会获取活动窗口并将其分配为所有者,但它是也不是 100% 可靠。
    • 谢谢。我不知道 ShowDialog 在这种情况下会产生如此丑陋的问题。你能指出一个链接/参考,它说 ShowDialog 会产生这样的问题吗?
    猜你喜欢
    • 2011-04-23
    • 1970-01-01
    • 1970-01-01
    • 2015-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    相关资源
    最近更新 更多