2016-04-08 14 views
9

Wir verwenden die Klassen im Namespace Microsoft.VisualStudio.XmlEditor (https://msdn.microsoft.com/en-us/library/microsoft.visualstudio.xmleditor.aspx), um ein XML-Dokument in einer Visual Studio-Erweiterung zu ändern.VSIX - Deadlock auf XmlEditingScope.Complete()

Aus irgendeinem Grund tritt nach dem Aufruf der Methode XmlEditingScope.Complete() ein Deadlock auf. In der Statusleiste von Visual Studio, sehen wir die Meldung "Waiting for Parsing zu vervollständigen ..."

Dies ist der Stack-Trace des Deadlock UI-Thread:

WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) 
mscorlib.dll!System.Threading.SynchronizationContext.InvokeWaitMethodHelper(System.Threading.SynchronizationContext syncContext, System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)  
[Native to Managed Transition] 
[Managed to Native Transition] 
mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext) 
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout, bool exitContext) 
Microsoft.VisualStudio.Package.LanguageService.14.0.dll!Microsoft.VisualStudio.Package.LanguageService.ParseWaitHandle.WaitOne(int millisecondsTimeout, bool exitContext)  
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.XmlLanguageService.WaitForParse(System.IAsyncResult result, Microsoft.XmlEditor.StatusBarIndicator indicator)  
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.XmlLanguageService.WaitForParse()  
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.XmlParserLock.XmlParserLock(Microsoft.XmlEditor.XmlLanguageService service) 
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.Transaction.PushToEditorTreeAndBuffer() 
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.Transaction.Complete() 
XmlEditingScope.Complete() Line 64 

Und das Visual Studio parsen thread:

mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext) + 0x21 bytes 
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout, bool exitContext) + 0x28 bytes  
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.LockManager.Lock(object resource, Microsoft.XmlEditor.LockMode mode, Microsoft.XmlEditor.Transaction txId) + 0x14c bytes  
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.TransactionManager.BeginParseSourceTransaction(Microsoft.XmlEditor.XmlSource src, Microsoft.XmlEditor.Transaction parent) + 0x9f bytes 
Microsoft.XmlEditor.dll!Microsoft.XmlEditor.XmlLanguageService.ParseSource(Microsoft.VisualStudio.Package.ParseRequest req) + 0x17d bytes  
Microsoft.VisualStudio.Package.LanguageService.14.0.dll!Microsoft.VisualStudio.Package.LanguageService.ParseRequest(Microsoft.VisualStudio.Package.ParseRequest req) + 0x75 bytes  
Microsoft.VisualStudio.Package.LanguageService.14.0.dll!Microsoft.VisualStudio.Package.LanguageService.ParseThread() + 0x140 bytes 
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x70 bytes  
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0xa7 bytes 
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x16 bytes 
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x41 bytes  
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes  
[Native to Managed Transition] 

es ist nicht einfach hier alle relevanten Codes zu erhalten, aber im Grunde ist es nur der folgende Code, der nach einer Änderung in einem WPF-Datagrid-Steuerelement (IEditableObject.EndEdit ausgeführt wird in ViewModel):

using (var s = store.BeginEditingScope("Test", null)) 
{ 
     apply changes in xmlModel.Document... 

     s.Complete(); 
} 

Was kann ich tun, um dieses Deadlock zu verhindern. Muss ich etwas sperren, bevor ich die Änderungen anwende? Was könnte ich sonst falsch machen?

Antwort

0

Es ist mehr ein Kommentar, aber passte nicht in ein Kommentarfeld. Es ist schwierig, den genauen Grund zu nennen, warum dies in Ihrem Fall passiert, oder einen Weg zu finden, es mit den von Ihnen bereitgestellten Informationen zu beheben (um mehr Hilfe zu bekommen, benötigen wir ein minimales Beispiel, das wir einfach ausführen und das Problem sehen können). Stacktraces zeigen jedoch, dass es sich hierbei um einen regulären UI-Deadlock handelt, der häufig in fast allen UI-Frameworks auftritt, weil sie "single threaded" sind (alle Aktionen mit UI-Elementen müssen auf einem einzelnen Thread ausgeführt werden). Thread A (in diesem Fall Visual Studio-Analysethread) sendet eine Aufgabe an die UI-Threadwarteschlange aus dem Hintergrundthread und wartet darauf, dass sie abgeschlossen wird (z. B. WPF Dispatcher.Invoke-Aufruf, der genau das tut). Es ist nicht notwendig, dass die gesamte Aufgabe auf dem UI-Thread ausgeführt wird, damit Deadlock auftritt, nur ein Teil davon (z. B. - tatsächliches XML aus UI-Steuerelement abrufen) ist ausreichend. Dann machen Sie das gleiche auf UI Thread selbst. Das sind Sie warten Sie auf auf einige warten, behandeln in UI-Thread (mit Sperranweisungen auf UI-Thread fällt in die gleiche Kategorie). Dies ist äußerst gefährlich und führt zu Deadlocks, die Sie (wahrscheinlich) in diesem Fall haben.

Ich werde mein Punkt mit diesem kleinen Beispiel (WPF) illustrieren:

public partial class MainWindow : Window { 
    private DummyXmlParser _test = new DummyXmlParser(); 
    public MainWindow() { 
     InitializeComponent(); 
     new Thread(() => { 
      _test.StartParseInBackground(); 
      _test.WaitHandle.WaitOne(); 
     }) { 
      IsBackground = true 
     }.Start(); 

     _test.StartParseInBackground(); 
     // don't do this, will deadlock 
     _test.WaitHandle.WaitOne(); 
    } 
} 

public class DummyXmlParser { 
    public DummyXmlParser() { 
     WaitHandle = new ManualResetEvent(false); 
    } 

    public void StartParseInBackground() { 
     Task.Run(() => { 
      Thread.Sleep(1000); 
      // this gets dispatched to UI thread, but UI thread is blocked by waiting on WaitHandle - deadlock 
      Application.Current.Dispatcher.Invoke(() => 
      { 
       Application.Current.MainWindow.Title = "Running at UI"; 
      }); 
      WaitHandle.Set(); 
     }); 
    } 

    public ManualResetEvent WaitHandle { get; private set; } 
} 

In Ihrem Fall scheint es XmlEditingScope.Complete auf UI-Thread läuft und wartet auf ParseWaitHandle, die Sie vermeiden sollten nur Verhalten. Um das Problem zu beheben, können Sie versuchen, die Ausführung des obigen Codes auf dem UI-Thread zu vermeiden und stattdessen im Hintergrundthread auszuführen.

+0

Evk, danke für deine Antwort. Ich habe versucht, XmlEditingScope auf einem Hintergrundthread auszuführen, aber dann erhalte ich eine System.AccessViolationException. bei Microsoft.VisualStudio.Shell.Interop.IVsQueryEditQuerySave2.QueryEditFiles (UInt32 rgfQueryEdit, Int32 cFiles, String [] rgpszMkDocuments, UInt32 [] rgrgf, VSQEQS_FILE_ATTRIBUTE_DATA [] rgFileInfo, UInt32 & pfEditVerdict, UInt32 & prgfMoreInfo) bei Microsoft.XmlEditor.Transaction.CanEditFilesInTransaction() bei Microsoft.XmlEditor.Transaction.PushToEditorTreeAndBuffer() bei Microsoft.XmlEditor.Transaction.Complete() – TWT

+0

Nun schwer, mehr Ratschläge ohne zusätzliche Informationen zu geben. Aber zumindest ist es jetzt nicht festgefahren :) Und was ist, wenn Sie es zum UI-Thread aber über Dispatcher.BeginInvoke (so asynchron) versenden? – Evk