Wat*_* v2 5 .net c# clr multithreading
为了好玩,我编写了这段代码来模拟死锁.然后,我坐着看着它耐心地运行,直到线程池已经降到零的可用工作线程的总数.我很想知道会发生什么.会抛出异常吗?
using System;
using System.Diagnostics;
using System.Threading;
namespace Deadlock
{
class Program
{
private static readonly object lockA = new object();
private static readonly object lockB = new object();
static void Main(string[] args)
{
int worker, io;
ThreadPool.GetAvailableThreads(out worker, out io);
Console.WriteLine($"Total number of thread pool threads: {worker}, {io}");
Console.WriteLine($"Total threads in my process: {Process.GetCurrentProcess().Threads.Count}");
Console.ReadKey();
try
{
for (int i = 0; i < 1000000; i++)
{
AutoResetEvent auto1 = new AutoResetEvent(false);
AutoResetEvent auto2 = new AutoResetEvent(false);
ThreadPool.QueueUserWorkItem(ThreadProc1, auto1);
ThreadPool.QueueUserWorkItem(ThreadProc2, auto2);
var allCompleted = WaitHandle.WaitAll(new[] { auto1, auto2 }, 20);
ThreadPool.GetAvailableThreads(out worker, out io);
var total = Process.GetCurrentProcess().Threads.Count;
if (allCompleted)
{
Console.WriteLine($"All threads done: (Iteration #{i + 1}). Total: {total}, Available: {worker}, {io}\n");
}
else
{
Console.WriteLine($"Timed out: (Iteration #{i + 1}). Total: {total}, Available: {worker}, {io}\n");
}
}
Console.WriteLine("Press any key to exit...");
}
catch(Exception ex)
{
Console.WriteLine("An exception occurred.");
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
Console.WriteLine("The program will now exit. Press any key to terminate the program...");
}
Console.ReadKey();
}
static void ThreadProc1(object state)
{
lock(lockA)
{
Console.WriteLine("ThreadProc1 entered lockA. Going to acquire lockB");
lock(lockB)
{
Console.WriteLine("ThreadProc1 acquired both locks: lockA and lockB.");
//Do stuff
Console.WriteLine("ThreadProc1 running...");
}
}
if (state != null)
{
((AutoResetEvent)state).Set();
}
}
static void ThreadProc2(object state)
{
lock(lockB)
{
Console.WriteLine("ThreadProc2 entered lockB. Going to acquire lockA.");
lock(lockA)
{
Console.WriteLine("ThreadProc2 acquired both locks: lockA and lockB.");
// Do stuff
Console.WriteLine("ThreadProc2 running...");
}
}
if (state != null)
{
((AutoResetEvent)state).Set();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
同时,我还保持Windows任务管理器的性能选项卡运行,并观察操作系统线程的总数,因为我的程序吃了更多的线程.
这是我观察到的:
由于.NET线程池每次都创建一个线程,因此操作系统不会创建更多线程.实际上,对于我的for循环运行的每四到五次迭代,操作系统线程计数会增加一到两次.这很有趣,但这不是我的问题.它证明了已经建立的东西.
更有趣的是,我观察到在我的for循环的每次迭代中线程数没有减少2 .我预计它应该已经下降了2,因为我的死锁线程没有被预期返回,因为它们已经陷入僵局,等待着彼此.
我还观察到,当线程池中可用工作线程的总数减少到零时,程序仍然继续运行我的for循环的更多迭代.这让我很好奇这些新线程来自哪里,如果线程池已经用完线程并且没有线程返回?
因此,为了澄清,我的两个问题,或许与单个答案可能是对它们的解释有关,是:
当我的for循环的单次迭代运行时,对于其中一些迭代,没有创建线程池线程.为什么?线程池在哪里获得线程来运行这些迭代?
当线程池耗尽其可用工作线程总数并仍然继续运行我的for循环时,线程池从何处获取?
ThreadPool.GetAvailableThreads(out worker, out io);
Run Code Online (Sandbox Code Playgroud)
这不是一个很好的统计数据,可以向您展示线程池的工作原理.主要问题是所有最新的.NET版本都是非常大的数字.在我的双核笔记本电脑上,它在32位模式下从1020开始,在64位模式下从32767开始.远远地,远远大于这种贫血CPU能够合理处理.这个数字多年来已经大幅膨胀,它开始时是.NET 2.0中核心数量的50倍.现在可以根据机器功能(CLR主机的作业)动态计算它.它使用的玻璃超过半满.
线程池管理器的主要工作是保持线程有效.最佳点是将执行线程的数量限制为处理器核心数量.运行更多会降低性能,操作系统必须在线程之间进行上下文切换,这会增加开销.
然而,这种理想并不总能得到满足,程序员编写的实用tp线程并不总是表现良好.在实践中,它们花费太长时间和/或花费太多时间来阻止I/O或锁而不是执行代码.你的例子当然是一个相当极端的阻塞案例.
线程池管理器不知道为什么 tp线程执行时间太长.它只能看到完成需要很长时间.深入了解线程花费太长时间的原因是不切实际的,它需要一个调试器和经过严格训练的大规模并行神经网络,程序员可以在他们的耳朵之间进行.
两次,线程池管理器重新评估工作负载,并允许额外的 tp线程在没有任何活动线程完成时启动.即使这超出了最佳范围.根据理论,这可能会完成更多的工作,因为可能是活动的阻塞太多而没有有效地使用可用的核心.解决一些死锁场景也很重要,尽管你永远不需要它.它只是一个常规线程,就像其他任何一个,底层的OS调用是CreateThread().
这就是你所看到的,可用线程的数量每秒下降两次.独立于您的代码,这是基于时间的.实际上在管理器中实现了一个反馈循环,它试图动态计算额外线程的最佳数量.你从来没有在那里阻止所有线程.
这不会永远持续下去,您最终会达到默认SetMaxThreads()设置的上限.没有例外,假设您没有首先遇到OutOfMemoryException并且您在现实生活中经常遇到,它只是停止添加更多线程.您仍然在向线程池添加执行请求,涵盖了子弹3,它们实际上从未真正开始.当请求数量过大时,最终会耗尽内存.你需要等待很长时间,需要一段时间才能填满一个千兆字节.
原因是 QueueUserWorkItem:“将一个方法排队等待执行。当线程池线程可用时,该方法将执行。” https://msdn.microsoft.com/en-us/library/kbf0f1ct(v=vs.110).aspx
根据我的理解,Threadpool只是慢慢增加线程数量以满足您的需求,这就是您在taskmgr中看到的。我认为您可以通过在线程中添加一些要做的事情来验证这一点。
编辑:我的意思是,你只需将它们排队,第一个线程就会启动,并且慢慢地(每 500 毫秒,https: //blogs.msdn.microsoft.com/pedram/2007/08/05/dedicated-thread-or -a-threadpool-thread/)越来越多的线程被添加,直到达到限制 - 之后您仍然可以对新线程进行排队。
| 归档时间: |
|
| 查看次数: |
978 次 |
| 最近记录: |