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中文網其他相關文章!