6

我有一个主 STA 线程,它在 COM 对象上调用很多方法,还有一个辅助 STA 线程,它也在同一个对象上做很多工作。我希望主线程和辅助线程并行工作(即我希望主线程和辅助线程的交错输出)。我知道我需要时不时地在主线程中发送消息——在 C++ 中调用 Get/Translate/DispatchMessage 就可以了。

但是我在 C# 中使用相同的策略时遇到问题。起初我在主线程中使用 CurrentThread.Join() 来控制第二个线程。它没有用。然后我转向 Application.DoEvents() - 每当我希望第二个线程运行时,我都会在主线程中调用它。结果是第二个线程迅速抓住了控制权并且不会放手——在第二个线程全部完成之前,主线程无法继续。

我读过文档说 Application.DoEvents() 将处理所有等待事件 - 而 GetMessage() 只检索一条消息。

正确的做法是什么?是否存在与 Get/Translate/DispatchMessage 等效的 C#?

谢谢

更新:第二个线程运行太快,向主 STA 线程发送大量 COM 调用消息。我只是在第二个线程中添加了延迟以减慢它的速度。现在两个线程基本上是并行运行的。但我仍然想知道是否存在与 GetMessage/TranslateMessage/DispatchMessage 等效的 C#。

4

2 回答 2

8

您的原始 C++ 代码违反了 STA 合同。其中规定接口指针必须从一个线程编组到另一个线程,以便对对象的所有调用仅从一个线程进行。这是单线程 COM 服务器的硬性要求,不这样做会冒与从多个线程对非线程安全代码进行调用相关的典型痛苦。使用两个 STA 线程并不能免除您的这一要求,该对象仅由创建它的线程拥有。第二个线程只是另一个线程,由于服务器不支持多线程,因此无法安全地调用它。

您以某种方式在 C++ 代码中侥幸逃脱,很难想象有时不会出现故障。COM 不能以其他方式在进程内 COM 服务器上强制执行 STA 合同,只能在进程外服务器上执行。在这种情况下,违反合约会生成 RPC_E_WRONG_THREAD。

Anyhoo,您再也不能在 C# 程序中逃脱惩罚了。CLR 会自动为您编组接口指针。您在第二个线程上进行的调用将被编组到拥有该对象的 STA 线程。仍然存在交错,但只有当第一个线程空闲并重新进入消息循环时,才能传递第二个线程的调用。对此没有解决方法,接口指针的 CLR 处理严格按照规则进行。

我想这将对您的代码产生很多影响,最大的一个是第二个线程真的不再完成任何事情了。没有并发,对对象的所有调用都是严格序列化的。和线程安全的。您最好只从一个线程进行所有调用,这样您就不必在相当大的死锁风险中跳舞。作为奖励,使正确的消息发送变得不那么重要。如果第二个线程执行其他关键工作,那么利用 COM 对单线程代码的支持可能会有所帮助。

于 2011-07-21T05:36:12.013 回答
4

至于 .Net 中的 Get/Translate/Dispatch,您应该能够在您的帮助线程上调用Application.RunDispatcher.Run()(取决于您使用的是 winforms 还是 wpf)。这将在为您调用 Get/Trans/Dispatch 的线程上生成一个消息循环。如果您讨厌这种想法,那么您可以 P/Invoke Win32 调用。

虽然 .Net 几乎可以按照hans 的回答为您完成所有工作,但实际上我发现来自 COM 对象的消息可能会导致您的 UI 线程卡顿,因为底层 DispatchMessage() 似乎需要很长时间(当然比我预期的要长,或者可以解释)。我们更改了解决方案,在帮助线程上创建 COM 对象,并显式地编组从 UI 线程对它的调用。

于 2011-07-21T06:07:20.683 回答