只是出于好玩,我编写了这段代码来模拟死锁。然后,我耐心地坐下来观察它的运行,直到线程池中可用工作线程的总数降为零。我很好奇会发生什么。它会抛出异常吗?
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();
}
}
}
}
同时,我也一直运行着Windows任务管理器的性能选项卡,并观察操作系统线程的总数随着我的程序占用更多线程而增加。
以下是我的观察结果:
由于每次.NET线程池创建一个新线程,操作系统并没有创建更多的线程。实际上,对于我的for循环运行的每四或五个迭代,操作系统的线程计数会增加一到两个。这很有趣,但这不是我的问题。它证明了已经被证实的内容。
更有趣的是,我观察到,我的for循环的每次迭代中线程数量并没有减少2个。我期望应该会减少2个,因为我所有死锁的线程都无法返回,因为它们正在互相等待。
我还观察到,当线程池中可用的工作线程总数降至零时,程序仍然继续运行我的for循环的更多迭代。这让我好奇,如果线程池中已经没有了线程,并且没有任何线程返回,那么新线程从哪里来?
因此,澄清一下,我的两个问题,也许是相关的,因为一个答案可能是它们的解释:
当我的for循环运行了一个迭代时,对于其中的某些迭代,没有创建线程池线程。为什么?线程池从哪里获取线程来运行这些迭代?
当线程池用尽了可用的工作线程总数并仍然保持运行我的for循环时,线程池从哪里获取线程?
Monitor.Enter
逻辑允许 锁定 的线程被重用。为什么不呢?反正线程正在等待,让我们在其中运行另一个任务等等。 - Sinatr