我有一个可能相当独特的问题。当我不在场时,我有一个应用程序可以在 headless 盒上运行很长时间,但并不重要。我希望能够使用 Visual Studio 远程调试此应用程序。为此,我有如下代码:
// 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();
想法是这样的,当我不在的时候遇到了一个错误,应用程序有效地暂停了自己并等待远程调试器附加,在第一次继续之后自动获得正确的上下文,这要归功于 Debugger .Break
调用。
这就是问题所在:实现 SuspendAllButCurrentThread
被证明是非常重要的。 Thread.Suspend
已弃用,我无法 P/Invoke 到 SuspendThread
因为托管线程和 native 线程之间没有一对一的映射(因为我需要使当前线程保持事件状态)。如果可以避免,我不想在有问题的机器上安装 Visual Studio。我怎样才能使它工作?
最佳答案
I can't P/Invoke down to SuspendThread because there's no one-to-one mapping between managed threads and native threads
您也不能枚举托管线程,只能枚举非托管线程。实际上是它们之间的一对一映射,只是很难找到它。最初的目的是允许创建一个不使用操作系统线程来实现 Thread 的自定义 CLR 主机,这是 SQL Server 组的一个请求,希望使用纤程来代替。这从来没有成功过,他们无法让它足够可靠。不使用实际操作系统线程的实际 CLR 主机不存在。
因此您实际上可以使用 Process.GetCurrentProcess().Threads 来枚举所有线程。并避免通过调用 GetCurrentThreadId() 来暂停您自己的,将其与 ProcessThread.Id 进行比较
这将是一个猜测有多可靠,不要尝试做 任何 激烈的事情,比如发送警报来提醒您是时候附加调试器了。您可能已经挂起一个在 Windows 中执行代码并获得全局锁的线程。以及 CLR 工作线程,如终结器线程或后台 GC 线程。
更好的方法是使用一个单独的保护进程来完成所有这些工作,就像调试器一样。使用您在守卫程序中创建的命名 EventWaitHandle 和主程序中的 OpenExisting()。守卫程序需要在该等待句柄和进程上使用 WaitAny()。您的主程序现在可以简单地调用 Set() 来唤醒守卫程序。现在可以安全地挂起所有线程。
关于C# 暂停所有线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18285909/