【问题标题】:See lots of clr!CLRSemaphore::Wait in call stack看到很多 clr!CLR Semaphore::Wait in call stack
【发布时间】:2016-08-12 01:55:14
【问题描述】:

我们看到很多像下面这样的调用堆栈,我可以知道什么条件\情况会发生这种情况吗?

OS Thread Id: 0x48654 (559)
Current frame: ntdll!NtWaitForSingleObject+0xa
Child-SP         RetAddr          Caller, Callee
00000020a76cf480 00007fffd4ea1118 KERNELBASE!WaitForSingleObjectEx+0x94, calling ntdll!NtWaitForSingleObject
00000020a76cf520 00007fffce50ce66 clr!CLRSemaphore::Wait+0x8a, calling kernel32!WaitForSingleObjectEx
00000020a76cf5e0 00007fffce50d247 clr!ThreadpoolMgr::UnfairSemaphore::Wait+0x109, calling clr!CLRSemaphore::Wait
00000020a76cf620 00007fffce50d330 clr!ThreadpoolMgr::WorkerThreadStart+0x1b9, calling clr!ThreadpoolMgr::UnfairSemaphore::Wait
00000020a76cf6c0 00007fffce5de8b6 clr!Thread::intermediateThreadProc+0x7d
00000020a76cfb40 00007fffce5de89f clr!Thread::intermediateThreadProc+0x66, calling clr!_chkstk
00000020a76cfb80 00007fffd60613d2 kernel32!BaseThreadInitThunk+0x22
00000020a76cfbb0 00007fffd7be5454 ntdll!RtlUserThreadStart+0x34
OS Thread Id: 0x3bd4c (560)
Current frame: ntdll!NtWaitForSingleObject+0xa
Child-SP         RetAddr          Caller, Callee
00000020a774e910 00007fffd4ea1118 KERNELBASE!WaitForSingleObjectEx+0x94, calling ntdll!NtWaitForSingleObject
00000020a774e9b0 00007fffce50ce66 clr!CLRSemaphore::Wait+0x8a, calling kernel32!WaitForSingleObjectEx
00000020a774ea70 00007fffce50d247 clr!ThreadpoolMgr::UnfairSemaphore::Wait+0x109, calling clr!CLRSemaphore::Wait
00000020a774eab0 00007fffce50d330 clr!ThreadpoolMgr::WorkerThreadStart+0x1b9, calling clr!ThreadpoolMgr::UnfairSemaphore::Wait
00000020a774eb50 00007fffce5de8b6 clr!Thread::intermediateThreadProc+0x7d
00000020a774ec30 00007fffd7c00c75 ntdll!RtlpLowFragHeapAllocFromContext+0x355, calling ntdll!memset

【问题讨论】:

  • 0:030> !do 0000001fc0a80fe8 名称:System.Runtime.Remoting.Contexts.Context MethodTable:00007fffcd2ae6c8 EEClass:00007fffccd74ad8 大小:104(0x68) 字节文件:C:\Windows\Microsoft.Net\ assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c5619 BTW,我们还看到线程上下文是 System.Runtime.Remoting.Contexts.Context
  • 仅根据调用堆栈中的函数名称,看起来就像一个空闲的工作线程——一个等待工作到达的线程。
  • 对于线程池线程来说这是一个完全正常的状态,它正在等待工作。如果您看到其中很多,那么之前出现了问题,您有太多的线程池请求需要很长时间才能完成。活动 TP 线程数的上限非常高。除了更好地平衡工作负载之外,理想的解决方案当然是 Q&D 修复是调用 ThreadPool.SetMaxThreads()。

标签: c# multithreading debugging threadpool windbg


【解决方案1】:

正如@Harry Johnston 在 cmets 中已经提到的,这些是线程池的工作线程,无关。

以下示例可用于复制此类堆栈。它将创建 12 个线程池工作线程,当调试器中断时,它们都处于您所看到的空闲状态。

代码基于微软的Fibunacci threadpool example:

using System.Diagnostics;
using System.Threading;

public class Fibonacci
{
    public void ThreadPoolCallback(object threadContext)
    {
        FibOfN = Calculate(N);
        DoneEvent.Set();
    }

    public int Calculate(int n)
    {
        if (n <= 1) return n;
        return Calculate(n - 1) + Calculate(n - 2);
    }

    public int N { get; set; }
    public int FibOfN { get; private set; }
    public ManualResetEvent DoneEvent { get; set; }
}

public class ClrSemaphoreWaitDemo
{
    static void Main()
    {
        const int numberOfTasks = 12;
        var doneEvents = new ManualResetEvent[numberOfTasks];
        var fibArray = new Fibonacci[numberOfTasks];
        ThreadPool.SetMaxThreads(numberOfTasks, numberOfTasks);
        ThreadPool.SetMinThreads(numberOfTasks, numberOfTasks);

        for (int i = 0; i < numberOfTasks; i++)
        {
            doneEvents[i] = new ManualResetEvent(false);
            fibArray[i] = new Fibonacci {N= 4, DoneEvent= doneEvents[i]};
            ThreadPool.QueueUserWorkItem(fibArray[i].ThreadPoolCallback, i);
        }

        WaitHandle.WaitAll(doneEvents);
        Debug.WriteLine("Now run .symfix; .reload; .loadby sos clr; !threads; !threads; !findstack clr!CLRSemaphore::Wait");
        Debugger.Break();
    }
}

当调试器中断时,运行以下命令:

.symfix; .reload; .loadby sos clr; !threads; !threads; !findstack clr!CLRSemaphore::Wait

【讨论】:

  • 您好Thomas,非常感谢您对我的几个主题的友好帮助,我会一一检查然后给出回复。再次感谢您。
  • Thomas ,所以这意味着在线程池线程完成他们的工作之后并且在它死\销毁之前,他们会调用 clr!CLRSemaphore::Wait 看看是否还有更多工作要做,并且如果他们保持那个状态并且在大约 20 秒内没有更多工作要做,他们就会死\摧毁,对吧?
  • 基本上是的,他们正在等待通过新工作到达时设置的信号量来等待新工作。在这种状态下,您无法判断他们是成功完成工作还是出了什么问题(例如发生了异常)。关于20秒:其实不知道过一段时间会不会被销毁。通过在调试器中断之前添加 Sleep() 调用,您可以轻松地尝试使用上面的代码。
  • 感谢您对这个话题的帮助,在我的测试中,如果没有工作,线程将在 20 秒后被销毁,如果我打开例如 100 个线程,则减少到 4 个线程。跨度>
  • 嗨,Thomas,我在线程 stackoverflow.com/questions/44615400/…stackoverflow.com/questions/44615400/… 中遇到了类似的问题,在 NETMON 呼吸之后,线程在短时间内增加到大量,并且这些线程在很久了,能帮忙查一下吗?
猜你喜欢
  • 2011-06-26
  • 2011-04-01
  • 2015-11-09
  • 2013-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
相关资源
最近更新 更多