6

Ich habe ein Problem, das ziemlich einzigartig sein kann. Ich habe eine Anwendung, die stundenlang auf einer kopflosen Box läuft, wenn ich nicht anwesend bin, aber nicht kritisch ist. Ich möchte in der Lage sein, diese Anwendung remote mit Visual Studio zu debuggen. Um dies zu tun, ich habe Code, der wie folgt aussieht:C# suspendiert alle Threads

// Suspend all other threads to prevent loss 
// of state while we investigate the issue. 
SuspendAllButCurrentThread(); 
var remoteDebuggerProcess = new Process 
    { 
     StartInfo = 
      { 
       UseShellExecute = true, 
       FileName = MsVsMonPath; 
      } 
    }; 
// Exception handling and early return removed here for brevity. 
remoteDebuggerProcess.Start(); 

// Wait for a debugger attach. 
while (!Debugger.IsAttached) 
{ 
    Thread.Sleep(500); 
} 
Debugger.Break(); 

// Once we get here, we've hit continue in the debugger. Restore all of our threads, 
// then get rid of the remote debugging tools. 
ResumeAllButCurrentThread(); 

remoteDebuggerProcess.CloseMainWindow(); 
remoteDebuggerProcess.WaitForExit(); 

Die Idee ist, dass auf diese Weise, ich einen Fehler getroffen, während ich weg bin, und die Anwendung Pausen selbst effektiv und wartet auf einen Remote-Debugger anhängen , die nach dem ersten fortfahren automatisch den richtigen Kontext dank der Debugger.Break Aufruf erhält.

Hier ist das Problem: Implementierung SuspendAllButCurrentThread erweist sich als nicht trivial. Thread.Suspend ist veraltet, und ich kann P/Invoke nicht bis SuspendThread aufrufen, da es keine Eins-zu-Eins-Zuordnung zwischen verwalteten Threads und nativen Threads gibt (da ich den aktuellen Thread am Leben halten muss). Ich möchte Visual Studio nicht auf dem fraglichen Computer installieren, wenn es möglicherweise vermieden werden kann. Wie kann ich das schaffen?

+0

Dumm Frage wahrscheinlich, aber VS nicht Remote Debugging ohne dass Sie durch diese Reifen springen müssen? Ich nahm immer an, dass Sie aus der Ferne ohne Code-Änderungen debuggen können, aber ich muss zugeben, ich habe nur lokal debuggt, – Surfbutler

Antwort

5

Ich kann nicht P/Invoke bis SuspendThread, weil es keine ist eine Eins-zu-Eins-Abbildung zwischen verwalteten Threads und native Threads

Sie können keine Threads entweder verwaltet aufzuzählen, nur nicht verwaltete Threads. Es ist tatsächlich ist eine Eins-zu-eins-Zuordnung zwischen ihnen, sie machten es nur schwer, es zu finden. Die ursprüngliche Absicht war es, einen benutzerdefinierten CLR-Host zu erstellen, der keine Betriebssystemthreads zur Implementierung von Thread verwendet, eine Anforderung der SQL Server-Gruppe, die stattdessen Fasern verwenden wollte. Das hat nie geklappt, sie konnten es nicht zuverlässig genug bekommen. Es existiert kein tatsächlicher CLR-Host, der keine echten Betriebssystem-Threads verwendet.

So können Sie tatsächlich Process.GetCurrentProcess() verwenden. Threads zum Auflisten aller Ihrer Threads. Und vermeiden Sie Ihre eigene Aussetzung von pinvoking GetCurrentThreadId(), ist es im Vergleich zu ProcessThread.Id

Wie zuverlässig das ist eine Vermutung sein wird, versuchen Sie nicht etwas drastisch zu tun, wie eine Benachrichtigung zu senden Sie daran zu erinnern, dass es ist Zeit, den Debugger anzuhängen. Möglicherweise haben Sie einen Thread, der Code innerhalb von Windows ausführte, ordnungsgemäß deaktiviert und eine globale Sperre erworben.Sowie ein CLR-Worker-Thread, wie der Finalizer-Thread oder der Hintergrund-GC-Thread.

Der bessere Ansatz ist es, einen separaten Guard-Prozess zu verwenden, der all dies tut, genau wie ein Debugger. Verwenden Sie ein benanntes EventWaitHandle, das Sie in dem Überwachungsprogramm und OpenExisting() in Ihrem Hauptprogramm erstellen. Das Guard-Programm muss WaitAny() auf diesem Wait-Handle sowie dem Prozess warten. Ihr Hauptprogramm kann jetzt einfach Set() aufrufen, um das Schutzprogramm zu aktivieren. Die können nun alle Threads sicher anhalten.

+0

Angenommen, dass ich etwas in Windows mit einer globalen Sperre aussetzen - wie vermeidet ein Debugger dieses Problem zu treffen? Zweitens, in diesem Fall würde ich auch den 'SuspendThread' aufrufen und nicht den veralteten 'Thread.Suspend' verwenden, oder? – Octavianus

+0

Es vermeidet es nicht. Deshalb muss ein Debugger immer ein separater Prozess sein. Ja, pinvoke es. –

+0

Okay, Sie meinen also eine globale Sperre, die für den Prozess spezifisch ist? Alternativ dazu gibt es eine Möglichkeit als Debugger für die Benachrichtigung eines anderen Debuggers zu registrieren, die versucht, anhängen? – Octavianus

1

Das Hauptproblem mit Thread.Suspend ist, dass es einige Objekte im unbrauchbaren Zustand belassen kann.

Vom documentation:

nicht die Methoden, Anhalten und Fortsetzen Verwenden Sie die Aktivitäten von Threads zu synchronisieren. Sie haben keine Möglichkeit zu wissen, welchen Code ein Thread ausführt, wenn Sie ihn anhalten. Wenn Sie einen Thread aussetzen, während Sperren während einer Sicherheitsberechtigungsauswertung enthält, werden möglicherweise andere Threads in der AppDomain blockiert. Wenn Sie einen Thread aussetzen, während einen Klassenkonstruktor ausführt, werden andere Threads in der AppDomain, die versuchen, diese Klasse zu verwenden, blockiert. Deadlocks können sehr einfach auftreten.

Wenn Sie versuchen, den Inhalt eines solchen unbrauchbaren Objekts anzuzeigen, werden Sie wahrscheinlich ebenfalls gesperrt. Daher können Sie unabhängig davon, was Sie zum Sperren anderer Threads verwenden, im selben Szenario landen. Und so besteht die einzige Möglichkeit darin, die Implementierung anderer Threads zu modifizieren, um sie zu veranlassen, sich selbst zu suspendieren: Is there a way to indefinitely pause a thread?.