C# 多线程进阶:线程池 + 事件等待
一、线程池ThreadPool1. 核心原理线程池是系统预先创建的固定线程容器用于复用线程。频繁 new Thread 会不断创建、销毁线程造成内存开销、GC压力线程池线程执行完毕不会销毁而是回归池内空闲待命供下次任务复用。使用方式直接投递任务给线程池无需手动创建、启动、销毁线程。2. 优缺点优点自动管理线程生命周期、复用线程、节省内存、避免频繁创建销毁线程的性能损耗。缺点无法获取线程实例、无法控制线程状态、不能挂起/恢复/优先级控制管控性差。3. 独立代码演示// 向线程池投递带参数的任务 ThreadPool.QueueUserWorkItem(s { Console.WriteLine(s); }, abc1); ThreadPool.QueueUserWorkItem(s { Console.WriteLine(s); }, abc2); ThreadPool.QueueUserWorkItem(s { Console.WriteLine(s); }, abc3);4. 注意事项线程池线程默认是后台线程窗口关闭自动终止不适合长时间阻塞任务会占用线程池线程导致后续任务排队无法获取 Thread 对象不能操作 Priority、Suspend、Resume适合大量短小、频繁执行的异步任务二、ManualResetEvent 手动事件等待线程阻塞与唤醒1. 核心原理ManualResetEvent是.NET 提供的线程信号控制类用来替代废弃的 Suspend/Resume安全实现线程阻塞与唤醒。依靠 bool 信号量控制阻塞状态false无信号WaitOne()阻塞线程线程暂停等待true有信号WaitOne()不阻塞线程正常通行核心方法Set()信号置为 true放行、唤醒线程Reset()信号置为 false阻塞线程WaitOne()根据信号决定是否阻塞当前线程2. 独立代码演示// 初始化默认无信号阻塞状态 ManualResetEvent eve new ManualResetEvent(false); // 线程池任务 ThreadPool.QueueUserWorkItem((s) { Thread.Sleep(1000); Console.WriteLine(s); eve.Set(); // 任务执行完毕发送信号解除阻塞 }, 罗志祥); // 主线程阻塞等待子线程信号 eve.WaitOne(); Console.WriteLine(AAAAAAAAAAAAAAAAAA);3. 执行流程主线程执行到WaitOne()卡住 → 子线程休眠1秒打印内容 → 调用Set()发送信号 → 主线程解除阻塞继续向下执行。4. 注意事项推荐使用事件信号控制线程启停淘汰 Suspend、Resume 过时APIManualResetEvent 一旦 Set 为 true会一直放行必须手动 Reset 才能再次阻塞可以精准控制线程执行顺序、任务同步等待三、ManualResetEventSlim 进阶实战进度条暂停/恢复/终止1. 核心原理ManualResetEventSlim是轻量化的事件信号类性能更高专门用于高频、短时间阻塞场景。通过 信号量 取消标记 实现完整线程状态控制Set() → 放行、进度条正常跑Reset() → 阻塞、进度条暂停自定义 isCancel 标记 Abort() → 彻底终止任务、重置进度条2. 完整可运行案例代码// 轻量化线程信号器 ManualResetEventSlim pauseEvent; Thread t null; // 全局进度条线程 bool isCancel false; // 任务取消标记 public Form1() { InitializeComponent(); // 初始化按钮状态 button2.Enabled false; button3.Enabled false; button4.Enabled false; // 创建进度条线程 t new Thread(updateProgressBar); // 默认有信号线程不阻塞 pauseEvent new ManualResetEventSlim(true); } // 进度条刷新逻辑 public void updateProgressBar(object a) { while (!isCancel t ! null Convert.ToInt32(a) 100) { // 根据信号阻塞/放行 pauseEvent.Wait(); Thread.Sleep(100); a Convert.ToInt32(a) 1; if (Convert.ToInt32(a) 100) { // 跨线程更新UI Invoke(new Action(() { progressBar1.Value Convert.ToInt32(a); })); } else { isCancel true; } } } // 开始任务 private void button1_Click(object sender, EventArgs e) { pauseEvent.Set(); // 放行信号 t.Start(0); button1.Enabled false; button2.Enabled true; button4.Enabled true; } // 暂停任务 private void button2_Click(object sender, EventArgs e) { pauseEvent.Reset(); // 阻塞线程 button3.Enabled true; button2.Enabled false; } // 恢复任务 private void button3_Click(object sender, EventArgs e) { pauseEvent.Set(); // 解除阻塞 button3.Enabled false; button2.Enabled true; button4.Enabled true; } // 终止并重置任务 private void button4_Click(object sender, EventArgs e) { isCancel true; t?.Abort(); // 重置界面 button4.Enabled false; button1.Enabled true; button2.Enabled false; button3.Enabled false; // 复位所有状态 pauseEvent.Set(); progressBar1.Value 0; t new Thread(updateProgressBar); isCancel false; } // 窗口关闭释放资源 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { isCancel true; pauseEvent.Set(); t.Abort(); pauseEvent.Dispose(); // 释放信号对象资源 }3. 完整运行逻辑开始Set() 放行进度条持续上涨暂停Reset() 阻塞进度条卡住不动恢复Set() 再次放行继续上涨终止取消标记 线程销毁 UI复位 重建线程可重复开启4. 注意事项ManualResetEventSlim 性能优于 ManualResetEvent日常开发优先使用信号阻塞是安全优雅的线程暂停方式完全替代过时的 Suspend/Resume窗口关闭必须手动 Dispose 释放事件对象防止内存泄漏线程终止后必须重新 new Thread否则无法二次启动通过标记位信号量双重控制避免线程卡死、报错四、知识点整体总结线程池复用线程、性能高无需手动管理线程生命周期适合大量短任务ManualResetEvent基础事件信号实现线程阻塞与等待控制任务同步ManualResetEventSlim轻量化高性能信号器实现工业级 暂停/恢复/终止 线程任务