【问题标题】:640 enterprise library caching threads - how?640 个企业库缓存线程 - 如何?
【发布时间】:2010-04-02 01:59:04
【问题描述】:

我们有一个正在进行性能测试的应用程序。今天,我决定转储 w3wp 并将其加载到 windbg 中,看看幕后发生了什么。想象一下当我运行 !threads 看到有 640 个后台线程时我的惊讶,几乎所有的线程似乎都在说以下内容:

OS Thread Id: 0x1c38 (651)
Child-SP         RetAddr          Call Site
0000000023a9d290 000007ff002320e2 Microsoft.Practices.EnterpriseLibrary.Caching.ProducerConsumerQueue.WaitUntilInterrupted()
0000000023a9d2d0 000007ff00231f7e Microsoft.Practices.EnterpriseLibrary.Caching.ProducerConsumerQueue.Dequeue()
0000000023a9d330 000007fef727c978 Microsoft.Practices.EnterpriseLibrary.Caching.BackgroundScheduler.QueueReader()
0000000023a9d380 000007fef9001552 System.Threading.ExecutionContext.runTryCode(System.Object)
0000000023a9dc30 000007fef72f95fd System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
0000000023a9dc80 000007fef9001552 System.Threading.ThreadHelper.ThreadStart()

如果我不得不猜测一下,我认为每次运行我们的应用程序都会产生其中一个线程 - 我们有 2 个应用程序服务器、20 个并发用户,并且运行了大约 30 次测试...就在附近。

这是“预期行为”,还是我们实施不当?测试是在几个小时前进行的,所以我预计已经发生了任何超时。

编辑:谢谢大家的回复。已要求显示有关调用堆栈的更多详细信息 - 这是来自 sosex.dll 的 !mk 的输出。


     ESP              RetAddr
00:U 0000000023a9cb38 00000000775f72ca ntdll!ZwWaitForMultipleObjects+0xa
01:U 0000000023a9cb40 00000000773cbc03 kernel32!WaitForMultipleObjectsEx+0x10b
02:U 0000000023a9cc50 000007fef8f5f595 mscorwks!WaitForMultipleObjectsEx_SO_TOLERANT+0xc1
03:U 0000000023a9ccf0 000007fef8f59f49 mscorwks!Thread::DoAppropriateAptStateWait+0x41
04:U 0000000023a9cd50 000007fef8e55b99 mscorwks!Thread::DoAppropriateWaitWorker+0x191
05:U 0000000023a9ce50 000007fef8e2efe8 mscorwks!Thread::DoAppropriateWait+0x5c
06:U 0000000023a9cec0 000007fef8f0dc7a mscorwks!CLREvent::WaitEx+0xbe
07:U 0000000023a9cf70 000007fef8fba72e mscorwks!Thread::Block+0x1e
08:U 0000000023a9cfa0 000007fef8e1996d mscorwks!SyncBlock::Wait+0x195
09:U 0000000023a9d0c0 000007fef9463d3f mscorwks!ObjectNative::WaitTimeout+0x12f
0a:M 0000000023a9d290 000007ff002321b3 *** ERROR: Module load completed but symbols could not be loaded for Microsoft.Practices.EnterpriseLibrary.Caching.DLL
Microsoft.Practices.EnterpriseLibrary.Caching.ProducerConsumerQueue.WaitUntilInterrupted()(+0x0 IL)(+0x11 Native)
0b:M 0000000023a9d2d0 000007ff002320e2 Microsoft.Practices.EnterpriseLibrary.Caching.ProducerConsumerQueue.Dequeue()(+0xf IL)(+0x18 Native)
0c:M 0000000023a9d330 000007ff00231f7e Microsoft.Practices.EnterpriseLibrary.Caching.BackgroundScheduler.QueueReader()(+0x9 IL)(+0x12 Native)
0d:M 0000000023a9d380 000007fef727c978 System.Threading.ExecutionContext.runTryCode(System.Object)(+0x18 IL)(+0x106 Native)
0e:U 0000000023a9d440 000007fef9001552 mscorwks!CallDescrWorker+0x82
0f:U 0000000023a9d490 000007fef8e9e5e3 mscorwks!CallDescrWorkerWithHandler+0xd3
10:U 0000000023a9d530 000007fef8eac83f mscorwks!MethodDesc::CallDescr+0x24f
11:U 0000000023a9d790 000007fef8f0cbd2 mscorwks!ExecuteCodeWithGuaranteedCleanupHelper+0x12a
12:U 0000000023a9da20 000007fef945e572 mscorwks!ReflectionInvocation::ExecuteCodeWithGuaranteedCleanup+0x172
13:M 0000000023a9dc30 000007fef7261722 System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)(+0x60 IL)(+0x51 Native)
14:M 0000000023a9dc80 000007fef72f95fd System.Threading.ThreadHelper.ThreadStart()(+0x8 IL)(+0x2a Native)
15:U 0000000023a9dcd0 000007fef9001552 mscorwks!CallDescrWorker+0x82
16:U 0000000023a9dd20 000007fef8e9e5e3 mscorwks!CallDescrWorkerWithHandler+0xd3
17:U 0000000023a9ddc0 000007fef8eac83f mscorwks!MethodDesc::CallDescr+0x24f
18:U 0000000023a9e010 000007fef8f9ae8d mscorwks!ThreadNative::KickOffThread_Worker+0x191
19:U 0000000023a9e330 000007fef8f59374 mscorwks!TypeHandle::GetParent+0x5c
1a:U 0000000023a9e380 000007fef8e52045 mscorwks!SVR::gc_heap::make_heap_segment+0x155
1b:U 0000000023a9e450 000007fef8f66139 mscorwks!ZapStubPrecode::GetType+0x39
1c:U 0000000023a9e490 000007fef8e1c985 mscorwks!ILCodeStream::GetToken+0x25
1d:U 0000000023a9e4c0 000007fef8f594e1 mscorwks!Thread::DoADCallBack+0x145
1e:U 0000000023a9e630 000007fef8f59399 mscorwks!TypeHandle::GetParent+0x81
1f:U 0000000023a9e680 000007fef8e52045 mscorwks!SVR::gc_heap::make_heap_segment+0x155
20:U 0000000023a9e750 000007fef8f66139 mscorwks!ZapStubPrecode::GetType+0x39
21:U 0000000023a9e790 000007fef8e20e15 mscorwks!ThreadNative::KickOffThread+0x401
22:U 0000000023a9e7f0 000007fef8e20ae7 mscorwks!ThreadNative::KickOffThread+0xd3
23:U 0000000023a9e8d0 000007fef8f814fc mscorwks!Thread::intermediateThreadProc+0x78
24:U 0000000023a9f7a0 00000000773cbe3d kernel32!BaseThreadInitThunk+0xd
25:U 0000000023a9f7d0 00000000775d6a51 ntdll!RtlUserThreadStart+0x1d

【问题讨论】:

    标签: multithreading enterprise-library windbg


    【解决方案1】:

    是的,缓存块有一些 - 问题 - 关于旧版本 Entlib 中的清理线程,特别是如果事情进入的速度比清理设置让它们出来的速度更快。

    这在 Entlib 5 中被完全重写,因此现在无论负载如何,缓存块中的线程都不会超过两个,而且通常只有一个。 不幸的是,没有简单的调整来改变早期版本中的行为。您可以做的最好的事情是更改缓存设置,以便每次清理将一次清理更多项目,这样就不需要安排那么多清理请求。

    【讨论】:

      【解决方案2】:

      640 线程对性能非常不利。如果他们都在等待某事,那么我敢打赌,你有一个僵局,他们永远不会退出。如果它们都在运行(不等待)......好吧,在 2 或 4 核处理器上有 600 多个线程,它们都不会获得足够的时间片来运行很远! ;>

      如果您的应用设置了一个主线程,该线程在线程句柄上等待以了解线程何时退出,而后台线程陷入循环或处于等待状态并且永远不会退出线程进程,那么进程及其所有线程永远不会退出。

      检查你的线程代码以确保每个线程进程都有一个清晰的路径来退出线程进程。假设在进程关闭时线程将被强制终止,在后台线程中编写无限循环是不好的形式。

      如果后台线程代码在循环中等待事件句柄发出信号,请确保您有某种方法可以发出该事件的信号,以便线程可以执行正常有序的退出。否则,您需要编写后台线程来等待多个事件并在任何一个事件发出信号时解除阻塞。其中一个事件可以是后台线程主要感兴趣的活动,另一个可以是关闭事件。

      从您发布的堆栈转储中的事物名称来看,该线程似乎正在等待某个事物出现在 ProducerConsumerQueue 中。调查该队列对象应该如何关闭,可能在生产者端,以及关闭队列是否会自动释放所有在该队列上等待的消费者。

      我的猜测是队列没有被正确关闭或者关闭它并不会隐式释放正在等待它的消费者。如果是后一种情况,您可能需要通过队列发送终止消息,以唤醒所有在该队列中等待的消费者,并告诉他们退出等待循环并退出。

      【讨论】:

        【解决方案3】:

        你有一个大问题。每个线程占用 1MB 的堆栈,并且为每个线程进出的上下文切换付出了巨大的成本。特别是托管代码变得最糟糕,因为每次 GC 必须运行时,它都会遍历线程堆栈以查找根,并且当这些线程被分页到磁盘时,从磁盘读取的成本是昂贵的,这增加了 Perf 问题.

        除非您知道自己在做什么,否则创建线程是不好的? Jeffery Richter 对此有详细的描述。

        为了解决上述问题,我会查看这些线程被阻塞的位置,并在 Thread Create 上设置一个断点(windbg 中的示例 sxe ct)

        后来重新架构避免创建线程,而是使用线程池。

        对这些线程的一些调用堆栈来说会很好。

        【讨论】:

        • 看起来他们正在等待事件(如 AutoReset 或 ManualReset),我没有看过这个库,我是从调用堆栈中猜测的。如果您在游戏中迟到要删除依赖项,我会将这个问题发布到 Library Dev 的论坛,以查看解决方法。如果你没有迟到,我会停止使用这个库,因为任何像这样设计的库都没有真正考虑过 Perf。这是我的两分钱
        【解决方案4】:

        在 Microsoft Enterprise Library 4.1 中,BackgroundScheduler 类在每次实例化对象时都会创建一个新线程。在 5.0 版中它将是 fixed。我对这个 Microsoft 库的了解不够多,无法建议您如何避免这种行为,但您可以尝试测试版:http://entlib.codeplex.com/wikipage?title=EntLib5%20Beta2

        【讨论】:

        • 谢谢。看起来 EntLib 5.0 将在 4 月 14 日发布 RTM,尽管在我们将项目迁移到它之前还需要相当长的时间。我需要让我的开发人员查看对象实例化。
        猜你喜欢
        • 2011-05-11
        • 2010-10-16
        • 2010-11-21
        • 1970-01-01
        • 1970-01-01
        • 2012-10-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多