停止时服务偶尔会挂起:挂起的线程
Service occasionally hangs when stopping: suspended threads
我用 C# 编写了一个针对 .NET 4.0 的 Windows 服务,当我尝试停止该服务时,有时会完全挂起。通过查看转储文件,我注意到我的许多线程已挂起,但我自己并没有在我的代码中挂起它们。
环境是 Windows Server 2008R2 64 位,尽管我在 Windows 7 64 位上观察到相同的挂起。 .NET 4.0 是安装的最新版本。
有很多代码,所以我只是 post 一些希望相关的片段,如果需要,我可以 post 更多。
基本设计:
Main() 启动一个新线程来处理记录到文件(其代码在单独的 dll 中),然后启动服务。
public static void Main(string[] args)
{
...
else if (Args.RunService)
{
Logger.Options.LogToFile = true;
MSPO.Logging.Logger.Start();
RunService();
MSPO.Logging.Logger.Stop();
}
...
}
private static void RunService()
{
service = new ProcessThrottlerService();
System.ServiceProcess.ServiceBase.Run(service);
}
该线程将保留在那里,直到 ServiceBase.Run returns.
服务中的 OnStart() 创建一个新线程并启动它。
protected override void OnStart(string[] args)
{
serviceThread = new MainServiceThread();
serviceThread.StartThread();
base.OnStart(args);
}
我创建了一个 ManualResetEventSlim,用作程序其余部分的停止信号。 OnStop() 设置事件。
protected override void OnStop()
{
if (serviceThread != null)
{
serviceThread.StopThread(); // Event is signalled in there
serviceThread.WaitForThreadToReturn(); // This calls thread.Join() on the MainServiceThread thread
}
base.OnStop();
}
"MainServiceThread" 创建事件,再次启动新线程,然后等待事件。
private void StartHandlerAndWaitForServiceStop()
{
processHandler.Start(serviceStopEvent);
serviceStopEvent.Wait();
processHandler.Stop();
}
processHandler 线程订阅此 WMI 查询:
watcher = new ManagementEventWatcher(new ManagementScope("root\CIMV2"),
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
watcher.EventArrived += HandleNewProcessCreated;
如果对新进程名称感兴趣,我会创建一个新的 "throttler" 线程,它实际上只是挂起进程、休眠、恢复进程,然后再次休眠,在一个循环中:
while (true)
{
ntresult = Ntdll.NtResumeProcess(processHandle);
if (ntresult != Ntdll.NTSTATUS.STATUS_SUCCESS)
{
if (ntresult != Ntdll.NTSTATUS.STATUS_PROCESS_IS_TERMINATING)
LogSuspendResumeFailure("resume", ntresult);
break;
}
Thread.Sleep(resumeTime);
ntresult = Ntdll.NtSuspendProcess(processHandle);
if (ntresult != Ntdll.NTSTATUS.STATUS_SUCCESS)
{
if (ntresult != Ntdll.NTSTATUS.STATUS_PROCESS_IS_TERMINATING)
LogSuspendResumeFailure("suspend", ntresult);
break;
}
Thread.Sleep(suspendTime);
if (++loop >= loopsBeforeCheckingStopEvent)
{
if (stopEvent.IsSet) break;
loop = 0;
}
}
如果服务收到停止命令,它将设置 ManualResetEventSlim 事件。任何线程 "throttling" 进程将在 1 秒内看到它并跳出 loop/return。进程处理程序线程将等待所有这些线程到 return,然后也是 return。那时 StartHandlerAndWaitForServiceStop() 方法 post 上面的 return 和其他一直在这里和那里等待的线程 return.
在我停止服务的绝大多数情况下,它都会毫无问题地停止。这与我是否有 0 或 500 个节流器线程 运行 无关,也与服务 运行.
时是否创建过节流器线程无关
然而,当我试图阻止它时(通过 services.msc),它会挂起。昨天,我设法在进程处于这种状态时创建了进程的完整转储。我使用 Process Explorer 创建了转储。
转储文件显示我的多个线程已挂起:
0:010> ~
0 Id: 1840.c34 Suspend: 0 Teb: 000007ff`fffdd000 Unfrozen
1 Id: 1840.548 Suspend: 0 Teb: 000007ff`fffdb000 Unfrozen
2 Id: 1840.9c0 Suspend: 0 Teb: 000007ff`fffd9000 Unfrozen
3 Id: 1840.1da8 Suspend: 0 Teb: 000007ff`fffd7000 Unfrozen
4 Id: 1840.b08 Suspend: 3 Teb: 000007ff`fffd5000 Unfrozen
5 Id: 1840.1b5c Suspend: 0 Teb: 000007ff`ffef6000 Unfrozen
6 Id: 1840.af0 Suspend: 2 Teb: 000007ff`ffef2000 Unfrozen
7 Id: 1840.c60 Suspend: 0 Teb: 000007ff`ffef0000 Unfrozen
8 Id: 1840.1d94 Suspend: 4 Teb: 000007ff`ffeee000 Unfrozen
9 Id: 1840.1cd8 Suspend: 4 Teb: 000007ff`ffeec000 Unfrozen
. 10 Id: 1840.1c64 Suspend: 0 Teb: 000007ff`ffefa000 Unfrozen
11 Id: 1840.1dc8 Suspend: 0 Teb: 000007ff`fffd3000 Unfrozen
12 Id: 1840.8f4 Suspend: 0 Teb: 000007ff`ffefe000 Unfrozen
这与我在 Process Explorer 中看到的有关 - 在我 "throttling" 的两个进程中,一个被永久挂起,另一个被永久恢复。因此,这些节流器线程实际上被挂起,因为它们不再工作。他们应该不可能不被暂停就停止,因为我有错误处理围绕它,任何异常都会导致这些线程记录信息和 return。此外,他们的调用堆栈没有显示任何错误。由于某些错误,他们没有永久休眠,因为两次休眠的休眠时间分别为 22 和 78 毫秒,在我尝试停止服务之前它工作正常。
所以我试图了解这些线程是如何暂停的。我唯一的怀疑是 GC,因为在 reclaiming/compacting 内存时挂起线程。
我在这里粘贴了 !eestack 和 ~*kb 的内容:http://pastebin.com/rfQK0Ak8
我应该提到我没有符号,因为在创建转储时我已经多次重建应用程序。但是,由于它是 .NET,我想这不是什么大问题?
来自 eestack,我认为这些是 "my" 个线程:
- 线程0:主服务线程,还在ServiceBase.Run方法中。
- 线程 4:这是我的记录器线程。该线程将花费其生命的大部分时间等待阻塞队列。
- 线程 6:我的 MainServiceThread 线程,它只是在等待要设置的事件。
- 线程 8 和 9:两者都是 "throttler" 线程,执行我在上面 post 编辑的循环。
- 线程 10:此线程似乎正在执行 OnStop() 方法,因此正在处理服务停止命令。
就是这样,根据转储文件,线程 4、6、8 和 9 被挂起。因此,除了主线程和处理 OnStop() 方法的线程外,所有 "my" 个线程都被挂起。
现在我不太了解 GC 和调试 .NET 的东西,但线程 10 对我来说看起来很狡猾。调用堆栈摘录:
Thread 10
Current frame: ntdll!NtWaitForMultipleObjects+0xa
Child-SP RetAddr Caller, Callee
000000001a83d670 000007fefdd41420 KERNELBASE!WaitForMultipleObjectsEx+0xe8, calling ntdll!NtWaitForMultipleObjects
000000001a83d6a0 000007fef4dc3d7c clr!CExecutionEngine::ClrVirtualAlloc+0x3c, calling kernel32!VirtualAllocStub
000000001a83d700 000007fefdd419bc KERNELBASE!WaitForMultipleObjectsEx+0x224, calling ntdll!RtlActivateActivationContextUnsafeFast
000000001a83d710 000007fef4e9d3aa clr!WKS::gc_heap::grow_heap_segment+0xca, calling clr!StressLog::LogOn
000000001a83d730 000007fef4e9cc98 clr!WKS::gc_heap::adjust_limit_clr+0xec, calling clr!memset
000000001a83d740 000007fef4df398d clr!COMNumber::FormatInt32+0x8d, calling clr!LazyMachStateCaptureState
000000001a83d750 000007fef4df398d clr!COMNumber::FormatInt32+0x8d, calling clr!LazyMachStateCaptureState
000000001a83d770 00000000778a16d3 kernel32!WaitForMultipleObjectsExImplementation+0xb3, calling kernel32!WaitForMultipleObjectsEx
000000001a83d7d0 000007fef4e9ce73 clr!WKS::gc_heap::allocate_small+0x158, calling clr!WKS::gc_heap::a_fit_segment_end_p
000000001a83d800 000007fef4f8f8e1 clr!WaitForMultipleObjectsEx_SO_TOLERANT+0x91, calling kernel32!WaitForMultipleObjectsExImplementation
000000001a83d830 000007fef4dfb798 clr!Thread::GetApartment+0x34, calling clr!GetThread
000000001a83d860 000007fef4f8f6ed clr!Thread::GetFinalApartment+0x1a, calling clr!Thread::GetApartment
000000001a83d890 000007fef4f8f6ba clr!Thread::DoAppropriateAptStateWait+0x56, calling clr!WaitForMultipleObjectsEx_SO_TOLERANT
000000001a83d8d0 000007fef4f8f545 clr!Thread::DoAppropriateWaitWorker+0x1b1, calling clr!Thread::DoAppropriateAptStateWait
000000001a83d990 000007fef4ecf167 clr!ObjectNative::Pulse+0x147, calling clr!HelperMethodFrameRestoreState
000000001a83d9d0 000007fef4f8f63b clr!Thread::DoAppropriateWait+0x73, calling clr!Thread::DoAppropriateWaitWorker
000000001a83da50 000007fef4f0ff6a clr!Thread::JoinEx+0xa6, calling clr!Thread::DoAppropriateWait
000000001a83dac0 000007fef4defd90 clr!GCHolderBase<0,0,0,0>::EnterInternal+0x3c, calling clr!Thread::EnablePreemptiveGC
000000001a83daf0 000007fef4f1039a clr!ThreadNative::DoJoin+0xd8, calling clr!Thread::JoinEx
000000001a83db20 000007fef45f86f3 (MethodDesc 000007fef3cbe8d8 +0x1a3 System.Threading.SemaphoreSlim.Release(Int32)), calling 000007fef4dc31b0 (stub for System.Threading.Monitor.Exit(System.Object))
000000001a83db60 000007fef4dfb2a6 clr!FrameWithCookie<HelperMethodFrame_1OBJ>::FrameWithCookie<HelperMethodFrame_1OBJ>+0x36, calling clr!GetThread
000000001a83db90 000007fef4f1024d clr!ThreadNative::Join+0xfd, calling clr!ThreadNative::DoJoin
000000001a83dc40 000007ff001723f5 (MethodDesc 000007ff001612c0 +0x85 MSPO.Logging.MessageQueue.EnqueueMessage(System.String)), calling (MethodDesc 000007fef30fde88 +0 System.Collections.Concurrent.BlockingCollection`1[[System.__Canon, mscorlib]].TryAddWithNoTimeValidation(System.__Canon, Int32, System.Threading.CancellationToken))
000000001a83dcf0 000007ff001720e9 (MethodDesc 000007ff00044bb0 +0xc9 ProcessThrottler.Logging.Logger.Log(LogLevel, System.String)), calling (MethodDesc 000007ff00161178 +0 MSPO.Logging.MessageFormatter.QueueFormattedOutput(System.String, System.String))
000000001a83dd10 000007fef4f101aa clr!ThreadNative::Join+0x5a, calling clr!LazyMachStateCaptureState
000000001a83dd30 000007ff0018000b (MethodDesc 000007ff00163e10 +0x3b ProcessThrottler.Service.MainServiceThread.WaitForThreadToReturn()), calling 000007fef4f10150 (stub for System.Threading.Thread.JoinInternal())
000000001a83dd60 000007ff0017ff44 (MethodDesc 000007ff00049f30 +0xc4 ProcessThrottler.Service.ProcessThrottlerService.OnStop()), calling 000007ff0004d278 (stub for ProcessThrottler.Service.MainServiceThread.WaitForThreadToReturn())
000000001a83dda0 000007fef63fcefb (MethodDesc 000007fef63d65e0 +0xbb System.ServiceProcess.ServiceBase.DeferredStop())
我可以 post 更多代码显示我的每个函数在做什么,但我真的不认为这是我的代码中的死锁,因为在那种情况下线程不会暂停。所以我正在查看上面的调用堆栈,并在我告诉它将字符串记录到队列后看到它正在执行一些 GC 操作。但是 none 的 GC 东西看起来很狡猾,至少与我在 http://blogs.msdn.com/b/tess/archive/2008/02/11/hang-caused-by-gc-xml-deadlock.aspx 中看到的相比没有 我有一个配置文件告诉它使用 gcServer,但我几乎可以肯定它没有使用该设置是因为在我之前的测试中 GCSettings.IsServerGC
总是 returned false.
所以...有人对我的线程被暂停的原因有任何建议吗?
顺便说一句,这是我的 OpenProcess 方法,它获取进程的句柄 suspended/resumed,以回应 Hans 的评论:
private void GetProcessHandle(CurrentProcessDetails process)
{
IntPtr handle = Kernel32.OpenProcess(
process.Settings.RequiredProcessAccessRights,
false,
(uint)process.ID
);
if (handle == IntPtr.Zero)
throw new Win32ExceptionWrapper(
string.Format("Failed to open process {0} {1}",
process.Settings.ProcessNameWithExt, process.IDString));
process.Handle = handle;
}
我已经找到原因了。它与我的代码无关。这是 Process Explorer 中的错误。
我的程序是针对 .NET 4.0 编写的。如果我使用 Process Explorer 查看我的任何线程的调用堆栈,Process Explorer 会挂起线程并且不会恢复它。它应该做的是在获取调用堆栈时挂起线程,然后立即恢复。但它不会恢复线程 - 无论如何都不会恢复我的托管线程。
我可以用这个非常简单的代码复制它:
using System;
namespace Test
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < int.MaxValue; i++)
{
Console.WriteLine(i.ToString());
}
}
}
}
如果我将其编译为目标 .NET 4.0 或更高版本,运行 它,并使用 Process Explorer 打开线程 运行 循环,线程将被挂起。恢复按钮将可用,我可以单击它恢复线程。多次开启线程导致多次挂起;我通过使用 Windbg 查看线程的挂起计数来确认这一点。
如果我将它编译为低于 4.0 的目标版本(尝试过 2.0 和 3.5),我在 Process Explorer 中打开的线程不会保持暂停状态。
我用 C# 编写了一个针对 .NET 4.0 的 Windows 服务,当我尝试停止该服务时,有时会完全挂起。通过查看转储文件,我注意到我的许多线程已挂起,但我自己并没有在我的代码中挂起它们。
环境是 Windows Server 2008R2 64 位,尽管我在 Windows 7 64 位上观察到相同的挂起。 .NET 4.0 是安装的最新版本。
有很多代码,所以我只是 post 一些希望相关的片段,如果需要,我可以 post 更多。
基本设计:
Main() 启动一个新线程来处理记录到文件(其代码在单独的 dll 中),然后启动服务。
public static void Main(string[] args)
{
...
else if (Args.RunService)
{
Logger.Options.LogToFile = true;
MSPO.Logging.Logger.Start();
RunService();
MSPO.Logging.Logger.Stop();
}
...
}
private static void RunService()
{
service = new ProcessThrottlerService();
System.ServiceProcess.ServiceBase.Run(service);
}
该线程将保留在那里,直到 ServiceBase.Run returns.
服务中的 OnStart() 创建一个新线程并启动它。
protected override void OnStart(string[] args)
{
serviceThread = new MainServiceThread();
serviceThread.StartThread();
base.OnStart(args);
}
我创建了一个 ManualResetEventSlim,用作程序其余部分的停止信号。 OnStop() 设置事件。
protected override void OnStop()
{
if (serviceThread != null)
{
serviceThread.StopThread(); // Event is signalled in there
serviceThread.WaitForThreadToReturn(); // This calls thread.Join() on the MainServiceThread thread
}
base.OnStop();
}
"MainServiceThread" 创建事件,再次启动新线程,然后等待事件。
private void StartHandlerAndWaitForServiceStop()
{
processHandler.Start(serviceStopEvent);
serviceStopEvent.Wait();
processHandler.Stop();
}
processHandler 线程订阅此 WMI 查询:
watcher = new ManagementEventWatcher(new ManagementScope("root\CIMV2"),
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
watcher.EventArrived += HandleNewProcessCreated;
如果对新进程名称感兴趣,我会创建一个新的 "throttler" 线程,它实际上只是挂起进程、休眠、恢复进程,然后再次休眠,在一个循环中:
while (true)
{
ntresult = Ntdll.NtResumeProcess(processHandle);
if (ntresult != Ntdll.NTSTATUS.STATUS_SUCCESS)
{
if (ntresult != Ntdll.NTSTATUS.STATUS_PROCESS_IS_TERMINATING)
LogSuspendResumeFailure("resume", ntresult);
break;
}
Thread.Sleep(resumeTime);
ntresult = Ntdll.NtSuspendProcess(processHandle);
if (ntresult != Ntdll.NTSTATUS.STATUS_SUCCESS)
{
if (ntresult != Ntdll.NTSTATUS.STATUS_PROCESS_IS_TERMINATING)
LogSuspendResumeFailure("suspend", ntresult);
break;
}
Thread.Sleep(suspendTime);
if (++loop >= loopsBeforeCheckingStopEvent)
{
if (stopEvent.IsSet) break;
loop = 0;
}
}
如果服务收到停止命令,它将设置 ManualResetEventSlim 事件。任何线程 "throttling" 进程将在 1 秒内看到它并跳出 loop/return。进程处理程序线程将等待所有这些线程到 return,然后也是 return。那时 StartHandlerAndWaitForServiceStop() 方法 post 上面的 return 和其他一直在这里和那里等待的线程 return.
在我停止服务的绝大多数情况下,它都会毫无问题地停止。这与我是否有 0 或 500 个节流器线程 运行 无关,也与服务 运行.
时是否创建过节流器线程无关然而,当我试图阻止它时(通过 services.msc),它会挂起。昨天,我设法在进程处于这种状态时创建了进程的完整转储。我使用 Process Explorer 创建了转储。
转储文件显示我的多个线程已挂起:
0:010> ~
0 Id: 1840.c34 Suspend: 0 Teb: 000007ff`fffdd000 Unfrozen
1 Id: 1840.548 Suspend: 0 Teb: 000007ff`fffdb000 Unfrozen
2 Id: 1840.9c0 Suspend: 0 Teb: 000007ff`fffd9000 Unfrozen
3 Id: 1840.1da8 Suspend: 0 Teb: 000007ff`fffd7000 Unfrozen
4 Id: 1840.b08 Suspend: 3 Teb: 000007ff`fffd5000 Unfrozen
5 Id: 1840.1b5c Suspend: 0 Teb: 000007ff`ffef6000 Unfrozen
6 Id: 1840.af0 Suspend: 2 Teb: 000007ff`ffef2000 Unfrozen
7 Id: 1840.c60 Suspend: 0 Teb: 000007ff`ffef0000 Unfrozen
8 Id: 1840.1d94 Suspend: 4 Teb: 000007ff`ffeee000 Unfrozen
9 Id: 1840.1cd8 Suspend: 4 Teb: 000007ff`ffeec000 Unfrozen
. 10 Id: 1840.1c64 Suspend: 0 Teb: 000007ff`ffefa000 Unfrozen
11 Id: 1840.1dc8 Suspend: 0 Teb: 000007ff`fffd3000 Unfrozen
12 Id: 1840.8f4 Suspend: 0 Teb: 000007ff`ffefe000 Unfrozen
这与我在 Process Explorer 中看到的有关 - 在我 "throttling" 的两个进程中,一个被永久挂起,另一个被永久恢复。因此,这些节流器线程实际上被挂起,因为它们不再工作。他们应该不可能不被暂停就停止,因为我有错误处理围绕它,任何异常都会导致这些线程记录信息和 return。此外,他们的调用堆栈没有显示任何错误。由于某些错误,他们没有永久休眠,因为两次休眠的休眠时间分别为 22 和 78 毫秒,在我尝试停止服务之前它工作正常。
所以我试图了解这些线程是如何暂停的。我唯一的怀疑是 GC,因为在 reclaiming/compacting 内存时挂起线程。
我在这里粘贴了 !eestack 和 ~*kb 的内容:http://pastebin.com/rfQK0Ak8
我应该提到我没有符号,因为在创建转储时我已经多次重建应用程序。但是,由于它是 .NET,我想这不是什么大问题?
来自 eestack,我认为这些是 "my" 个线程:
- 线程0:主服务线程,还在ServiceBase.Run方法中。
- 线程 4:这是我的记录器线程。该线程将花费其生命的大部分时间等待阻塞队列。
- 线程 6:我的 MainServiceThread 线程,它只是在等待要设置的事件。
- 线程 8 和 9:两者都是 "throttler" 线程,执行我在上面 post 编辑的循环。
- 线程 10:此线程似乎正在执行 OnStop() 方法,因此正在处理服务停止命令。
就是这样,根据转储文件,线程 4、6、8 和 9 被挂起。因此,除了主线程和处理 OnStop() 方法的线程外,所有 "my" 个线程都被挂起。
现在我不太了解 GC 和调试 .NET 的东西,但线程 10 对我来说看起来很狡猾。调用堆栈摘录:
Thread 10
Current frame: ntdll!NtWaitForMultipleObjects+0xa
Child-SP RetAddr Caller, Callee
000000001a83d670 000007fefdd41420 KERNELBASE!WaitForMultipleObjectsEx+0xe8, calling ntdll!NtWaitForMultipleObjects
000000001a83d6a0 000007fef4dc3d7c clr!CExecutionEngine::ClrVirtualAlloc+0x3c, calling kernel32!VirtualAllocStub
000000001a83d700 000007fefdd419bc KERNELBASE!WaitForMultipleObjectsEx+0x224, calling ntdll!RtlActivateActivationContextUnsafeFast
000000001a83d710 000007fef4e9d3aa clr!WKS::gc_heap::grow_heap_segment+0xca, calling clr!StressLog::LogOn
000000001a83d730 000007fef4e9cc98 clr!WKS::gc_heap::adjust_limit_clr+0xec, calling clr!memset
000000001a83d740 000007fef4df398d clr!COMNumber::FormatInt32+0x8d, calling clr!LazyMachStateCaptureState
000000001a83d750 000007fef4df398d clr!COMNumber::FormatInt32+0x8d, calling clr!LazyMachStateCaptureState
000000001a83d770 00000000778a16d3 kernel32!WaitForMultipleObjectsExImplementation+0xb3, calling kernel32!WaitForMultipleObjectsEx
000000001a83d7d0 000007fef4e9ce73 clr!WKS::gc_heap::allocate_small+0x158, calling clr!WKS::gc_heap::a_fit_segment_end_p
000000001a83d800 000007fef4f8f8e1 clr!WaitForMultipleObjectsEx_SO_TOLERANT+0x91, calling kernel32!WaitForMultipleObjectsExImplementation
000000001a83d830 000007fef4dfb798 clr!Thread::GetApartment+0x34, calling clr!GetThread
000000001a83d860 000007fef4f8f6ed clr!Thread::GetFinalApartment+0x1a, calling clr!Thread::GetApartment
000000001a83d890 000007fef4f8f6ba clr!Thread::DoAppropriateAptStateWait+0x56, calling clr!WaitForMultipleObjectsEx_SO_TOLERANT
000000001a83d8d0 000007fef4f8f545 clr!Thread::DoAppropriateWaitWorker+0x1b1, calling clr!Thread::DoAppropriateAptStateWait
000000001a83d990 000007fef4ecf167 clr!ObjectNative::Pulse+0x147, calling clr!HelperMethodFrameRestoreState
000000001a83d9d0 000007fef4f8f63b clr!Thread::DoAppropriateWait+0x73, calling clr!Thread::DoAppropriateWaitWorker
000000001a83da50 000007fef4f0ff6a clr!Thread::JoinEx+0xa6, calling clr!Thread::DoAppropriateWait
000000001a83dac0 000007fef4defd90 clr!GCHolderBase<0,0,0,0>::EnterInternal+0x3c, calling clr!Thread::EnablePreemptiveGC
000000001a83daf0 000007fef4f1039a clr!ThreadNative::DoJoin+0xd8, calling clr!Thread::JoinEx
000000001a83db20 000007fef45f86f3 (MethodDesc 000007fef3cbe8d8 +0x1a3 System.Threading.SemaphoreSlim.Release(Int32)), calling 000007fef4dc31b0 (stub for System.Threading.Monitor.Exit(System.Object))
000000001a83db60 000007fef4dfb2a6 clr!FrameWithCookie<HelperMethodFrame_1OBJ>::FrameWithCookie<HelperMethodFrame_1OBJ>+0x36, calling clr!GetThread
000000001a83db90 000007fef4f1024d clr!ThreadNative::Join+0xfd, calling clr!ThreadNative::DoJoin
000000001a83dc40 000007ff001723f5 (MethodDesc 000007ff001612c0 +0x85 MSPO.Logging.MessageQueue.EnqueueMessage(System.String)), calling (MethodDesc 000007fef30fde88 +0 System.Collections.Concurrent.BlockingCollection`1[[System.__Canon, mscorlib]].TryAddWithNoTimeValidation(System.__Canon, Int32, System.Threading.CancellationToken))
000000001a83dcf0 000007ff001720e9 (MethodDesc 000007ff00044bb0 +0xc9 ProcessThrottler.Logging.Logger.Log(LogLevel, System.String)), calling (MethodDesc 000007ff00161178 +0 MSPO.Logging.MessageFormatter.QueueFormattedOutput(System.String, System.String))
000000001a83dd10 000007fef4f101aa clr!ThreadNative::Join+0x5a, calling clr!LazyMachStateCaptureState
000000001a83dd30 000007ff0018000b (MethodDesc 000007ff00163e10 +0x3b ProcessThrottler.Service.MainServiceThread.WaitForThreadToReturn()), calling 000007fef4f10150 (stub for System.Threading.Thread.JoinInternal())
000000001a83dd60 000007ff0017ff44 (MethodDesc 000007ff00049f30 +0xc4 ProcessThrottler.Service.ProcessThrottlerService.OnStop()), calling 000007ff0004d278 (stub for ProcessThrottler.Service.MainServiceThread.WaitForThreadToReturn())
000000001a83dda0 000007fef63fcefb (MethodDesc 000007fef63d65e0 +0xbb System.ServiceProcess.ServiceBase.DeferredStop())
我可以 post 更多代码显示我的每个函数在做什么,但我真的不认为这是我的代码中的死锁,因为在那种情况下线程不会暂停。所以我正在查看上面的调用堆栈,并在我告诉它将字符串记录到队列后看到它正在执行一些 GC 操作。但是 none 的 GC 东西看起来很狡猾,至少与我在 http://blogs.msdn.com/b/tess/archive/2008/02/11/hang-caused-by-gc-xml-deadlock.aspx 中看到的相比没有 我有一个配置文件告诉它使用 gcServer,但我几乎可以肯定它没有使用该设置是因为在我之前的测试中 GCSettings.IsServerGC
总是 returned false.
所以...有人对我的线程被暂停的原因有任何建议吗?
顺便说一句,这是我的 OpenProcess 方法,它获取进程的句柄 suspended/resumed,以回应 Hans 的评论:
private void GetProcessHandle(CurrentProcessDetails process)
{
IntPtr handle = Kernel32.OpenProcess(
process.Settings.RequiredProcessAccessRights,
false,
(uint)process.ID
);
if (handle == IntPtr.Zero)
throw new Win32ExceptionWrapper(
string.Format("Failed to open process {0} {1}",
process.Settings.ProcessNameWithExt, process.IDString));
process.Handle = handle;
}
我已经找到原因了。它与我的代码无关。这是 Process Explorer 中的错误。
我的程序是针对 .NET 4.0 编写的。如果我使用 Process Explorer 查看我的任何线程的调用堆栈,Process Explorer 会挂起线程并且不会恢复它。它应该做的是在获取调用堆栈时挂起线程,然后立即恢复。但它不会恢复线程 - 无论如何都不会恢复我的托管线程。
我可以用这个非常简单的代码复制它:
using System;
namespace Test
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < int.MaxValue; i++)
{
Console.WriteLine(i.ToString());
}
}
}
}
如果我将其编译为目标 .NET 4.0 或更高版本,运行 它,并使用 Process Explorer 打开线程 运行 循环,线程将被挂起。恢复按钮将可用,我可以单击它恢复线程。多次开启线程导致多次挂起;我通过使用 Windbg 查看线程的挂起计数来确认这一点。
如果我将它编译为低于 4.0 的目标版本(尝试过 2.0 和 3.5),我在 Process Explorer 中打开的线程不会保持暂停状态。