6

Ho un problema che può essere abbastanza unico. Ho un'applicazione che funziona su una casella senza testa per lunghe ore quando non sono presente, ma non è critica. Mi piacerebbe essere in grado di eseguire il debug di questa applicazione in remoto utilizzando Visual Studio. Per fare ciò, ho il codice che assomiglia a questo:C# che sospende tutti i thread

// 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(); 

L'idea è che in questo modo, mi ha colpito un errore durante la mia assenza, e l'applicazione stessa pause e attese per un debugger remoto collegare in modo efficace , che dopo la prima continua ottiene automaticamente il contesto giusto grazie alla chiamata Debugger.Break.

Ecco il problema: l'implementazione di SuspendAllButCurrentThread risulta non banale. Thread.Suspend è deprecato e non riesco a inoltrare P/Invoke a SuspendThread perché non esiste un mapping uno a uno tra thread gestiti e thread nativi (poiché è necessario mantenere attivo il thread corrente). Non voglio installare Visual Studio sulla macchina in questione se può essere evitato. Come posso fare questo lavoro?

+0

Domanda stupida probabilmente, ma VS non consente il debug remoto senza che sia necessario saltare attraverso questi cerchi? Ho sempre pensato che fosse possibile eseguire il debug remoto senza modifiche al codice, ma devo ammettere che ho sempre eseguito il debug in locale, – Surfbutler

risposta

5

non posso P/Invoke verso il basso per SuspendThread perché non c'è la mappatura uno-a-uno tra i thread gestiti e le discussioni nativi

Non è possibile enumerare gestito discussioni sia, solo discussioni non gestiti. In realtà è una mappatura uno-a-uno tra loro, hanno solo reso difficile trovarlo. L'intento originale era di consentire la creazione di un host CLR personalizzato che non utilizzava thread del sistema operativo per implementare Thread, una richiesta del gruppo SQL Server che voleva invece utilizzare fibre. Non funzionava mai, non potevano essere abbastanza affidabili. Non esiste alcun host CLR effettivo che non utilizzi thread del sistema operativo reali.

Quindi è possibile utilizzare Process.GetCurrentProcess(). Thread per enumerare tutti i thread. Ed evitare la sospensione proprio per pinvoking GetCurrentThreadId(), paragonandolo a ProcessThread.Id

Quanto è affidabile che sta per essere è una supposizione, non cercare di fare qualcosa di drastico come l'invio di un avviso per ricordare che è il momento di collegare il debugger. Potresti aver sospeso un thread che stava eseguendo il codice all'interno di Windows e acquisito un blocco globale.Oltre a un thread di lavoro CLR, come il thread di finalizzazione o il thread in background di GC.

L'approccio migliore consiste nell'utilizzare un processo di protezione separato che fa tutto questo, proprio come farà un debugger. Usa un evento EventWaitHandle che crei nel programma di guardia e OpenExisting() nel tuo programma principale. Il programma di protezione deve attendere WaitAny() su quell'attesa e sul processo. Il tuo programma principale ora può semplicemente chiamare Set() per riattivare il programma di guardia. Quale ora può tranquillamente sospendere tutti i thread.

+0

Supponiamo di sospendere qualcosa all'interno di Windows con un blocco globale: come fa un debugger a evitare questo problema? In secondo luogo, in questo caso, vorrei P/Richiama anche il 'SuspendThread', piuttosto che usare il' Thread.Suspend' deprecato, giusto? – Octavianus

+0

Non lo evita. Ecco perché un debugger deve sempre essere un processo separato. Sì, lo faccio. –

+0

Ok, quindi intendi un blocco globale specifico per il processo? In alternativa a questo, c'è un modo come un debugger per registrare la notifica di un altro debugger che tenta di allegare? – Octavianus

1

Il problema principale con Thread.Suspend è che può lasciare alcuni oggetti in uno stato inutilizzabile.

Dal documentation:

Non utilizzare i metodi di sospendere e riprendere per sincronizzare i attività di fili. Non hai modo di sapere quale codice è in esecuzione una discussione quando la si sospende. Se si sospende un thread mentre contiene blocchi durante una valutazione dell'autorizzazione di sicurezza, altri thread in AppDomain potrebbero essere bloccati. Se si sospende un thread mentre è nell'esecuzione di un costruttore di classi, altri thread nell'AppDomain che il tentativo di di utilizzare quella classe sono bloccati. Deadlock possono verificarsi molto facilmente .

Così, quando tenterai di visualizzare il contenuto di un oggetto così inutilizzato probabilmente verrai bloccato. Pertanto, indipendentemente da ciò che usi per sospendere altri thread, potresti finire nello stesso scenario. E così, l'unica possibilità è quella di modificare l'implementazione di altri thread per poter chiedere loro di sospendere se stessi: Is there a way to indefinitely pause a thread?.