简体   繁体   English

为什么我们不能更改 ThreadPool 线程的单元状态,为什么在使用 ShowDialog 时我们不需要消息泵?

[英]Why can't we change apartment state of a ThreadPool thread and why don't we require a messagepump when ShowDialog is used?

Recently, I encountered this situation where I wanted to display a form on another thread (not the main/UI thread).最近遇到这种情况,我想在另一个线程(不是主/UI线程)上显示一个表单。 I used a threadpool thread.我使用了线程池线程。 The form hosted a RCW (for a COM component).该表单承载了一个 RCW(用于 COM 组件)。 Instantiating the form gave me an exception that the thread must be a STA.实例化表单给了我一个异常,线程必须是一个 STA。 I tried to set the apartment state as STA.我试图将公寓状态设置为 STA。 But, that didn't work either.但是,这也不起作用。 I finally ended up creating a thread explicitly and that worked(I used ShowDialog and needn't create a meesage pump).我最终明确地创建了一个线程并且它起作用了(我使用了 ShowDialog 并且不需要创建一个 meesage pump)。

EDIT:编辑:

  • I know that threadpool threads are MTA.我知道线程池线程是 MTA。 But, why can't it be set to STA?但是,为什么不能设置为STA呢? Just curious about this.就是好奇这个。

  • Another question that just popped in my head: Why don't we require a message pump when Showdialog() (to display a form) is used另一个刚刚出现在我脑海中的问题:为什么在使用 Showdialog()(显示表单)时我们不需要消息泵

The apartment is selected by a call to CoInitializeEx().公寓是通过调用 CoInitializeEx() 来选择的。 A thread in the thread pool has already made that call, changing the apartment after that call is not possible.线程池中的一个线程已经进行了该调用,在该调用之后无法更改单元。

That a thread pool would choose MTA makes sense, it is after all intended as a worker thread and shouldn't be blocked by method calls that need to be marshaled.线程池选择 MTA 是有道理的,毕竟它是作为工作线程使用的,不应被需要编组的方法调用阻塞。 Selecting a single-threaded apartment has the additional requirement of pumping a message loop.选择单线程单元有一个额外的要求,即泵送消息循环。 Something you'd never expect a threadpool thread to do.你永远不会期望线程池线程做的事情。

The message loop is necessary because that's the vehicle that COM uses to marshal a call made on another thread.消息循环是必要的,因为这是 COM 用来编组在另一个线程上进行的调用的工具。 That call has to be "injected" in the STA thread, that's only possible if the thread is in a known quiescent state.该调用必须在 STA 线程中“注入”,只有当线程处于已知的静止状态时才有可能。 If it isn't, such a call would cause major re-entrancy problems.如果不是,这样的调用会导致严重的重入问题。 Which it sometimes does even if the thread is pumping the loop.即使线程正在泵送循环,它有时也会这样做。

You didn't need to pump a message loop yourself with Application.Run() because ShowDialog() starts its own message loop.您不需要自己使用 Application.Run() 来创建消息循环,因为 ShowDialog() 会启动它自己的消息循环。 That's how it gains modality.这就是它获得模态的方式。 That nested loop exits as soon as the dialog closes.一旦对话框关闭,嵌套循环就会退出。

You shouldn't rely on specific behavior for thread pool threads.您不应该依赖线程池线程的特定行为。 In general, a thread in the threadpool should be able to be replaced at any time by the CLR, without your knowledge.通常,线程池中的线程应该能够在您不知情的情况下随时被 CLR 替换。 Thread pool threads are meant to be used with simple tasks, preferably short lived ones.线程池线程旨在用于简单的任务,最好是短期任务。

If you want to have fine grained control over thread settings, you should create a dedicated thread.如果您想对线程设置进行细粒度控制,您应该创建一个专用线程。 Setting the apartment state is a perfect example of this.设置公寓状态就是一个很好的例子。


In addition to the above, theoretical reasons, there's a practical problem with what you are attempting.除了上述理论原因外,您尝试的操作还存在实际问题。 Hosting a form on a second thread doesn't work (without a lot of extra work in place).在第二个线程上托管表单不起作用(没有大量额外的工作)。 Forms must be run on the same thread as the message pump - otherwise, they won't receive any windows messages, and will not update properly.表单必须与消息泵在同一线程上运行 - 否则,它们将不会收到任何 Windows 消息,也不会正确更新。

You can create a form on a separate thread if you implement a full message pump for that thread, but it's usually a better idea to just put your work items onto background threads, and use asyncrhonous programming techniques to keep your since UI thread responsive.如果您为该线程实现完整的消息泵,您可以在单独的线程上创建一个表单,但将您的工作项放在后台线程上通常是一个更好的主意,并使用异步编程技术来保持自 UI 线程响应。

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

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