C# 中的线程池
C# 中的线程池
在本文中,我将通过示例讨论C# 中的线程池。请阅读我们之前的文章,其中我们讨论了C# 中**多线程应用程序的性能测试** **。**作为本文的一部分,我们将讨论以下几点。
- 线程的请求生命周期。
- C# 中的线程池
- 普通线程和线程池之间的性能测试
一、C# 中线程的请求生命周期及其示例。
当.NET框架收到请求时(该请求可以是来自任何类型的应用程序的方法调用或函数调用)。为了处理该请求,创建了一个线程对象。创建线程对象时,一些资源会分配给该线程对象,例如内存。然后执行任务,一旦任务完成,垃圾收集器就会删除该线程对象以释放内存分配。这就是C#中线程的生命周期。
对于多线程应用程序中出现的每个请求,将一次又一次地重复这些步骤。这意味着每次创建新的线程对象时,都会在内存中分配它。如果有很多请求,那么就会有很多线程对象,如果有很多线程对象,那么内存上就会有负载,这会减慢应用程序的速度。性能还有很大的提升空间。创建了 Thread 对象,分配了资源,执行了任务,然后它不应该进行垃圾回收,而不是像下图所示获取线程对象并将其放入池中?这就是线程池发挥作用的地方。
二、C# 中的线程池:
C# 中的线程池只不过是线程的集合,可以重用这些线程来在后台执行许多任务。现在当有请求到来时,它会直接进入线程池并检查是否有可用的空闲线程。如果可用,则它从线程池中获取线程对象并执行任务,如下图所示。
一旦线程完成其任务,它就会再次被发送回线程池,以便可以重用。这种可重用性避免了应用程序创建多个线程,从而减少了内存消耗。
三、普通线程和线程池之间的性能测试
如下面的代码所示,在这里,创建了一个方法 MyMethod,作为该方法的一部分,只需打印线程 id、线程是否为后台线程以及是否来自线程池或。使用线程池线程执行此方法 10 次。
3.1 线程池实现:
using System;
using System.Threading;
namespace ThreadPoolApplication
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(MyMethod));
}
Console.Read();
}
public static void MyMethod(object obj)
{
Thread thread = Thread.CurrentThread;
string message = $"Background: {thread.IsBackground}, Thread Pool: {thread.IsThreadPoolThread}, Thread ID: {thread.ManagedThreadId}";
Console.WriteLine(message);
}
}
}
执行上述代码后,它将给出以下输出。表明它是后台线程,并且线程来自线程池,并且输出中的线程 ID 可能有所不同。在这里,一些线程被重用,这就是在 C# 中使用线程池的优点。
3. 2 普通线程实现:
using System;
using System.Threading;
namespace ThreadPoolApplication
{
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(MyMethod)
{
Name = "Thread" + i
};
thread.Start();
}
Console.Read();
}
public static void MyMethod(object obj)
{
Thread thread = Thread.CurrentThread;
string message = $"Background: {thread.IsBackground}, Thread Pool: {thread.IsThreadPoolThread}, Thread ID: {thread.ManagedThreadId}";
Console.WriteLine(message);
}
}
}
运行上面的代码,您将得到以下输出。在这里,线程不是后台线程,它们也不是来自线程池,并且线程也没有被重用。每个线程都有一个唯一的线程ID。
3.3 线程与线程池性能对比
using System;
using System.Diagnostics;
using System.Threading;
namespace ThreadPoolApplication
{
class Program
{
static void Main(string[] args)
{
//Warmup Code start
for (int i = 0; i < 10; i++)
{
MethodWithThread();
MethodWithThreadPool();
}
//Warmup Code start
Stopwatch stopwatch = new Stopwatch();
Console.WriteLine("Execution using Thread");
stopwatch.Start();
MethodWithThread();
stopwatch.Stop();
Console.WriteLine("Time consumed by MethodWithThread is : " +
stopwatch.ElapsedTicks.ToString());
stopwatch.Reset();
Console.WriteLine("Execution using Thread Pool");
stopwatch.Start();
MethodWithThreadPool();
stopwatch.Stop();
Console.WriteLine("Time consumed by MethodWithThreadPool is : " +
stopwatch.ElapsedTicks.ToString());
Console.Read();
}
public static void MethodWithThread()
{
for (int i = 0; i < 10; i++)
{
Thread thread = new Thread(Test);
thread.Start();
}
}
public static void MethodWithThreadPool()
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Test));
}
}
public static void Test(object obj)
{
}
}
}
正如输出中看到的,MethodWithThread 消耗的时间是 272588,而 MethodWithThreadPool 消耗的时间是 692。这两者之间存在巨大的时间差异。因此,它证明与线程类对象相比,线程池提供了更好的性能。如果需要创建一两个线程,那么需要使用Thread类对象,而如果需要创建超过5个线程,那么需要在多线程环境中使用线程池类。