17、线程池

线程池

知识点概述

线程池是.NET Framework提供的重要特性,用于管理和重用线程资源。线程池可以显著提高应用程序性能,避免频繁创建和销毁线程的开销。

主要特点:

  • 线程重用: 避免重复创建销毁线程的开销
  • 自动管理: 系统自动调整线程池大小
  • 工作项队列: 使用队列管理待执行的任务
  • 后台线程: 线程池中的线程都是后台线程
  • 线程数限制: 有最大和最小线程数限制

代码案例

案例1:ThreadPool基本使用

using System;
using System.Threading;

class ThreadPoolBasicDemo
{
    static void Main()
    {
        Console.WriteLine("ThreadPool 基本使用演示:");
        
        // 获取线程池信息
        int workerThreads, completionPortThreads;
        ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
        Console.WriteLine($"最大工作线程数: {workerThreads}");
        Console.WriteLine($"最大I/O完成线程数: {completionPortThreads}");
        
        ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
        Console.WriteLine($"最小工作线程数: {workerThreads}");
        Console.WriteLine($"最小I/O完成线程数: {completionPortThreads}");
        
        Console.WriteLine("\n向线程池提交工作项:");
        
        // 向线程池提交工作
        for (int i = 1; i <= 5; i++)
        {
            int taskId = i;
            ThreadPool.QueueUserWorkItem(DoWork, taskId);
        }
        
        // 等待所有工作完成
        Thread.Sleep(3000);
        Console.WriteLine("所有任务完成");
    }
    
    static void DoWork(object state)
    {
        int taskId = (int)state;
        int threadId = Thread.CurrentThread.ManagedThreadId;
        
        Console.WriteLine($"任务 {taskId} 在线程 {threadId} 上开始执行");
        
        // 模拟工作
        Thread.Sleep(1000);
        
        Console.WriteLine($"任务 {taskId} 在线程 {threadId} 上完成");
    }
}

案例2:使用ManualResetEvent等待线程池任务完成

using System;
using System.Threading;

class ThreadPoolWithSynchronization
{
    private static ManualResetEvent[] _resetEvents;
    private static int _taskCount = 4;
    
    static void Main()
    {
        _resetEvents = new ManualResetEvent[_taskCount];
        
        Console.WriteLine($"启动 {_taskCount} 个线程池任务:");
        
        // 启动多个任务
        for (int i = 0; i < _taskCount; i++)
        {
            _resetEvents[i] = new ManualResetEvent(false);
            int taskIndex = i;
            
            ThreadPool.QueueUserWorkItem(ProcessTask, taskIndex);
        }
        
        Console.WriteLine("等待所有任务完成...");
        
        // 等待所有任务完成
        WaitHandle.WaitAll(_resetEvents);
        
        Console.WriteLine("所有任务已完成!");
        
        // 清理资源
        foreach (var resetEvent in _resetEvents)
        {
            resetEvent.Dispose();
        }
    }
    
    static void ProcessTask(object state)
    {
        int taskIndex = (int)state;
        int threadId = Thread.CurrentThread.ManagedThreadId;
        
        Console.WriteLine($"任务 {taskIndex} 开始 (线程ID: {threadId})");
        
        // 模拟不同长度的工作
        Random rand = new Random(taskIndex);
        int workTime = rand.Next(1000, 3000);
        Thread.Sleep(workTime);
        
        Console.WriteLine($"任务 {taskIndex} 完成 (耗时: {workTime}ms, 线程ID: {threadId})");
        
        // 标记任务完成
        _resetEvents[taskIndex].Set();
    }
}

案例3:线程池性能对比

using System;
using System.Threading;
using System.Diagnostics;
using System.Collections.Generic;

class ThreadPoolPerformanceComparison
{
    static void Main()
    {
        int taskCount = 20;
        
        Console.WriteLine($"性能对比测试 - 执行 {taskCount} 个任务");
        Console.WriteLine("====================================");
        
        // 测试普通线程创建
        TestRegularThreads(taskCount);
        
        // 等待一下再测试线程池
        Thread.Sleep(1000);
        
        // 测试线程池
        TestThreadPool(taskCount);
    }
    
    static void TestRegularThreads(int taskCount)
    {
        Console.WriteLine("\n1. 使用普通Thread:");
        Stopwatch sw = Stopwatch.StartNew();
        
        List<Thread> threads = new List<Thread>();
        
        for (int i = 0; i < taskCount; i++)
        {
            int taskId = i;
            Thread thread = new Thread(() => SimulateWork(taskId, "Thread"));
            threads.Add(thread);
            thread.Start();
        }
        
        // 等待所有线程完成
        foreach (Thread thread in threads)
        {
            thread.Join();
        }
        
        sw.Stop();
        Console.WriteLine($"普通线程总耗时: {sw.ElapsedMilliseconds}ms");
    }
    
    static void TestThreadPool(int taskCount)
    {
        Console.WriteLine("\n2. 使用ThreadPool:");
        Stopwatch sw = Stopwatch.StartNew();
        
        ManualResetEvent[] resetEvents = new ManualResetEvent[taskCount];
        
        for (int i = 0; i < taskCount; i++)
        {
            resetEvents[i] = new ManualResetEvent(false);
            int taskId = i;
            
            ThreadPool.QueueUserWorkItem(_ =>
            {
                SimulateWork(taskId, "ThreadPool");
                resetEvents[taskId].Set();
            });
        }
        
        // 等待所有任务完成
        WaitHandle.WaitAll(resetEvents);
        
        sw.Stop();
        Console.WriteLine($"线程池总耗时: {sw.ElapsedMilliseconds}ms");
        
        // 清理资源
        foreach (var resetEvent in resetEvents)
        {
            resetEvent.Dispose();
        }
    }
    
    static void SimulateWork(int taskId, string threadType)
    {
        int threadId = Thread.CurrentThread.ManagedThreadId;
        Console.WriteLine($"{threadType} 任务 {taskId} 开始 (线程ID: {threadId})");
        
        // 模拟CPU密集型工作
        long sum = 0;
        for (int i = 0; i < 1000000; i++)
        {
            sum += i;
        }
        
        Console.WriteLine($"{threadType} 任务 {taskId} 完成 (线程ID: {threadId})");
    }
}

案例4:调整线程池设置

using System;
using System.Threading;
using System.Diagnostics;

class ThreadPoolConfiguration
{
    static void Main()
    {
        Console.WriteLine("线程池配置演示:");
        
        // 显示当前线程池设置
        DisplayThreadPoolInfo();
        
        Console.WriteLine("\n调整线程池设置:");
        
        // 设置最小线程数
        ThreadPool.SetMinThreads(4, 4);
        
        // 设置最大线程数(注意:通常不建议减小最大线程数)
        int maxWorkerThreads, maxCompletionPortThreads;
        ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
        ThreadPool.SetMaxThreads(Math.Min(maxWorkerThreads, 8), maxCompletionPortThreads);
        
        DisplayThreadPoolInfo();
        
        Console.WriteLine("\n测试线程池行为:");
        TestThreadPoolBehavior();
    }
    
    static void DisplayThreadPoolInfo()
    {
        int workerThreads, completionPortThreads;
        int availableWorkerThreads, availableCompletionPortThreads;
        
        ThreadPool.GetMaxThreads(out workerThreads, out completionPortThreads);
        ThreadPool.GetMinThreads(out availableWorkerThreads, out availableCompletionPortThreads);
        
        Console.WriteLine($"最大线程数 - 工作线程: {workerThreads}, I/O线程: {completionPortThreads}");
        Console.WriteLine($"最小线程数 - 工作线程: {availableWorkerThreads}, I/O线程: {availableCompletionPortThreads}");
        
        ThreadPool.GetAvailableThreads(out availableWorkerThreads, out availableCompletionPortThreads);
        Console.WriteLine($"可用线程数 - 工作线程: {availableWorkerThreads}, I/O线程: {availableCompletionPortThreads}");
    }
    
    static void TestThreadPoolBehavior()
    {
        int taskCount = 10;
        CountdownEvent countdown = new CountdownEvent(taskCount);
        
        Console.WriteLine($"提交 {taskCount} 个任务到线程池:");
        
        for (int i = 0; i < taskCount; i++)
        {
            int taskId = i;
            ThreadPool.QueueUserWorkItem(_ =>
            {
                int threadId = Thread.CurrentThread.ManagedThreadId;
                Console.WriteLine($"任务 {taskId} 开始执行 (线程ID: {threadId}) - {DateTime.Now:HH:mm:ss.fff}");
                
                Thread.Sleep(2000); // 模拟工作
                
                Console.WriteLine($"任务 {taskId} 完成 (线程ID: {threadId}) - {DateTime.Now:HH:mm:ss.fff}");
                countdown.Signal();
            });
        }
        
        countdown.Wait();
        Console.WriteLine("所有任务完成");
        countdown.Dispose();
    }
}

案例5:线程池中的异常处理

using System;
using System.Threading;

class ThreadPoolExceptionHandling
{
    static void Main()
    {
        Console.WriteLine("线程池异常处理演示:");
        
        // 设置未处理异常事件处理器
        AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
        
        // 提交会抛出异常的任务
        for (int i = 0; i < 3; i++)
        {
            int taskId = i;
            ThreadPool.QueueUserWorkItem(FaultyTask, taskId);
        }
        
        // 提交正常任务
        for (int i = 3; i < 6; i++)
        {
            int taskId = i;
            ThreadPool.QueueUserWorkItem(SafeTask, taskId);
        }
        
        Thread.Sleep(3000);
        Console.WriteLine("主线程继续执行");
    }
    
    static void FaultyTask(object state)
    {
        int taskId = (int)state;
        
        try
        {
            Console.WriteLine($"有问题的任务 {taskId} 开始");
            
            if (taskId % 2 == 0)
            {
                throw new InvalidOperationException($"任务 {taskId} 故意抛出异常");
            }
            
            Thread.Sleep(1000);
            Console.WriteLine($"有问题的任务 {taskId} 正常完成");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"任务 {taskId} 异常被捕获: {ex.Message}");
        }
    }
    
    static void SafeTask(object state)
    {
        int taskId = (int)state;
        
        try
        {
            Console.WriteLine($"安全任务 {taskId} 开始");
            Thread.Sleep(800);
            Console.WriteLine($"安全任务 {taskId} 完成");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"安全任务 {taskId} 意外异常: {ex.Message}");
        }
    }
    
    static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Console.WriteLine($"未处理的异常: {e.ExceptionObject}");
    }
}

案例6:监控线程池状态

using System;
using System.Threading;
using System.Threading.Tasks;

class ThreadPoolMonitoring
{
    private static bool _monitoring = true;
    
    static async Task Main()
    {
        Console.WriteLine("线程池监控演示:");
        
        // 启动监控任务
        Task monitorTask = Task.Run(MonitorThreadPool);
        
        // 模拟工作负载
        await SimulateWorkload();
        
        // 停止监控
        _monitoring = false;
        await monitorTask;
        
        Console.WriteLine("监控结束");
    }
    
    static void MonitorThreadPool()
    {
        Console.WriteLine("开始监控线程池状态...\n");
        
        while (_monitoring)
        {
            int availableWorkerThreads, availableCompletionPortThreads;
            int maxWorkerThreads, maxCompletionPortThreads;
            
            ThreadPool.GetAvailableThreads(out availableWorkerThreads, out availableCompletionPortThreads);
            ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
            
            int busyWorkerThreads = maxWorkerThreads - availableWorkerThreads;
            int busyCompletionPortThreads = maxCompletionPortThreads - availableCompletionPortThreads;
            
            Console.Clear();
            Console.WriteLine("线程池状态监控");
            Console.WriteLine("================");
            Console.WriteLine($"工作线程: {busyWorkerThreads}/{maxWorkerThreads} 正在使用");
            Console.WriteLine($"I/O线程: {busyCompletionPortThreads}/{maxCompletionPortThreads} 正在使用");
            Console.WriteLine($"可用工作线程: {availableWorkerThreads}");
            Console.WriteLine($"可用I/O线程: {availableCompletionPortThreads}");
            Console.WriteLine($"监控时间: {DateTime.Now:HH:mm:ss}");
            
            Thread.Sleep(500);
        }
    }
    
    static async Task SimulateWorkload()
    {
        Console.WriteLine("开始模拟工作负载...");
        
        // 阶段1:轻负载
        for (int i = 0; i < 3; i++)
        {
            ThreadPool.QueueUserWorkItem(_ => Thread.Sleep(2000));
        }
        await Task.Delay(3000);
        
        // 阶段2:中等负载
        for (int i = 0; i < 6; i++)
        {
            ThreadPool.QueueUserWorkItem(_ => Thread.Sleep(3000));
        }
        await Task.Delay(4000);
        
        // 阶段3:高负载
        for (int i = 0; i < 12; i++)
        {
            ThreadPool.QueueUserWorkItem(_ => Thread.Sleep(2000));
        }
        await Task.Delay(5000);
    }
}

知识点总结

  1. 资源重用: 线程池通过重用线程减少创建/销毁开销,提高性能
  2. 自动管理: 系统根据负载自动调整线程池大小
  3. 工作项队列: 使用FIFO队列管理待执行的任务
  4. 配置选项: 可以调整最大/最小线程数,但要谨慎设置
  5. 异常处理: 线程池中的异常需要在任务内部处理
  6. 性能监控: 监控线程池状态有助于优化应用程序性能
  7. 使用场景: 适合短时间运行的任务,长时间运行的任务应使用专用线程
posted @ 2025-08-27 01:24  jessqiu  阅读(38)  评论(0)    收藏  举报