StaTaskScheduler 和 STA 线程消息泵送
来自并行团队的 ParallelExtensionsExtras 中的 StaTaskScheduler 旨在托管第三方提供的旧版 STA COM 对象。实现说明指出,它支持 MTA 和 STA 线程,并处理底层 API(如 WaitHandle.WaitAll)的差异。
然而,假设 StaTaskScheduler 会使用一个泵送消息的等待 API(例如 CoWaitForMultipleHandles)来防止 STA 线程上的死锁是不正确的。TPL 的阻塞部分可能不会泵送消息,从而导致死锁。
在一个简化的场景中,进程内 STA COM 对象 A 调用进程外对象 B 并期望从 B 获取回调,问题就出现了。由于 BlockingCollection
解决方案是实现一个自定义同步上下文,该上下文使用 CoWaitForMultipleHandles 显式泵送消息,并将其安装在 StaTaskScheduler 启动的每个 STA 线程上。
MsgWaitForMultipleObjectsEx 比 CoWaitForMultipleHandles 更适合消息泵送。自定义同步上下文的 Wait 方法可以实现为转发到 SynchronizationContext.WaitHelper 或包含一个功能齐全的消息泵送循环。
以下代码演示了一个包含消息泵送循环的自定义同步上下文实现:
<code class="language-c#">// 核心循环 var msg = new NativeMethods.MSG(); while (true) { // 带有 MWMO_INPUTAVAILABLE 的 MsgWaitForMultipleObjectsEx 返回, // 即使消息队列中已经看到但未删除消息 nativeResult = NativeMethods.MsgWaitForMultipleObjectsEx( count, waitHandles, (uint)remainingTimeout, QS_MASK, NativeMethods.MWMO_INPUTAVAILABLE); if (IsNativeWaitSuccessful(count, nativeResult, out managedResult) || WaitHandle.WaitTimeout == managedResult) return managedResult; // 有消息,泵送并分派它 if (NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, NativeMethods.PM_REMOVE)) { NativeMethods.TranslateMessage(ref msg); NativeMethods.DispatchMessage(ref msg); } if (hasTimedOut()) return WaitHandle.WaitTimeout; }</code>
使用此自定义同步上下文和消息泵送循环可确保即使在 STA 线程上使用阻塞等待时也能泵送消息并防止 STA 线程死锁。
以上是使用带有阻塞等待的 StaTaskScheduler 时,如何防止 STA 线程出现死锁?的详细内容。更多信息请关注PHP中文网其他相关文章!