StaTaskScheduler and STA thread message pumping
The StaTaskScheduler in ParallelExtensionsExtras from the Parallel team is designed to host legacy STA COM objects provided by third parties. The implementation description states that it supports MTA and STA threads and handles differences in underlying APIs such as WaitHandle.WaitAll.
However, it is incorrect to assume that the StaTaskScheduler will use a wait API that pumps messages (such as CoWaitForMultipleHandles) to prevent deadlocks on the STA thread. The blocking portion of the TPL may not pump messages, causing a deadlock.
In a simplified scenario, the problem arises when in-process STA COM object A calls out-of-process object B and expects to get a callback from B. The call to a.Method(b) never returns due to a blocking wait (not pumping messages) somewhere inside BlockingCollection
The solution is to implement a custom synchronization context that explicitly pumps messages using CoWaitForMultipleHandles and install it on every STA thread started by the StaTaskScheduler.
MsgWaitForMultipleObjectsEx is better suited for message pumping than CoWaitForMultipleHandles. A custom synchronization context's Wait method can be implemented as a forwarder to SynchronizationContext.WaitHelper or contain a fully functional message pumping loop.
The following code demonstrates a custom synchronization context implementation that includes a message pumping loop:
<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>
Using this custom synchronization context and message pumping loop ensures that messages are pumped even when using blocking waits on the STA thread and prevents STA thread deadlock.
The above is the detailed content of How Can Deadlocks Be Prevented on STA Threads When Using StaTaskScheduler with Blocking Waits?. For more information, please follow other related articles on the PHP Chinese website!