StaTaskScheduler und STA-Thread-Nachrichtenpumpen
Der StaTaskScheduler in ParallelExtensionsExtras vom Parallel-Team ist für das Hosten älterer STA COM-Objekte von Drittanbietern konzipiert. In der Implementierungsbeschreibung heißt es, dass sie MTA- und STA-Threads unterstützt und Unterschiede in zugrunde liegenden APIs wie WaitHandle.WaitAll verarbeitet.
Es ist jedoch falsch anzunehmen, dass der StaTaskScheduler eine Warte-API verwendet, die Nachrichten pumpt (z. B. CoWaitForMultipleHandles), um Deadlocks im STA-Thread zu verhindern. Der blockierende Teil der TPL pumpt möglicherweise keine Nachrichten weiter, was zu einem Deadlock führt.
In einem vereinfachten Szenario tritt das Problem auf, wenn das prozessinterne STA-COM-Objekt A das prozessinterne Objekt B aufruft und einen Rückruf von B erwartet. Der Aufruf von a.Method(b) kehrt nie zurück, da irgendwo innerhalb von BlockingCollection
Die Lösung besteht darin, einen benutzerdefinierten Synchronisierungskontext zu implementieren, der Nachrichten mithilfe von CoWaitForMultipleHandles explizit pumpt, und ihn auf jedem vom StaTaskScheduler gestarteten STA-Thread zu installieren.
MsgWaitForMultipleObjectsEx eignet sich besser zum Nachrichtenpumpen als CoWaitForMultipleHandles. Die Wait-Methode eines benutzerdefinierten Synchronisierungskontexts kann als Weiterleitung an SynchronizationContext.WaitHelper implementiert werden oder eine voll funktionsfähige Nachrichtenpumpschleife enthalten.
Der folgende Code demonstriert eine benutzerdefinierte Synchronisierungskontextimplementierung, die eine Message-Pump-Schleife enthält:
<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>
Durch die Verwendung dieses benutzerdefinierten Synchronisierungskontexts und der Nachrichtenpumpschleife wird sichergestellt, dass Nachrichten auch dann gepumpt werden, wenn blockierende Wartevorgänge im STA-Thread verwendet werden, und verhindert einen STA-Thread-Deadlock.
Das obige ist der detaillierte Inhalt vonWie können Deadlocks bei STA-Threads verhindert werden, wenn StaTaskScheduler mit blockierenden Wartezeiten verwendet wird?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!