news 2026/8/2 0:18:17

08-C#

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
08-C#

C#.Net-Thread-学习笔记


一、多线程基础概念

多线程的三大特点:

  • 异步执行:不阻塞主线程,多件事可以同时进行
  • 效率高:充分利用 CPU 等计算机资源
  • 无序性:多个线程的执行顺序不可预测,无法控制

⚠️ 正因为无序性,多线程调试困难,通常只能通过写日志、输出结果、结合线程 ID 来分析问题。

// 获取当前线程 ID,用于区分不同线程 int id = Thread.CurrentThread.ManagedThreadId;

二、Thread 类

基本概念

  • 来源System.Threading命名空间
  • 出现时间:.NET Framework 1.0
  • 类型:密封类(sealed class),不可被继承
  • 作用:操作计算机线程资源的帮助类库

开启线程

方式一:ThreadStart(无参数)

ThreadStart threadStart = new ThreadStart(() => { DoSomething("Richard"); }); Thread thread = new Thread(threadStart); thread.Start();

方式二:ParameterizedThreadStart(带参数)

ParameterizedThreadStart pts = new ParameterizedThreadStart(obj => { Console.WriteLine($"参数: {obj}"); }); Thread thread = new Thread(pts); thread.Start("传递的参数");

常用 API

线程控制
thread.Suspend(); // 暂停线程(不会立即停止) thread.Resume(); // 恢复执行 thread.Abort(); // 停止线程(向线程抛出异常) Thread.ResetAbort(); // 取消停止,让线程继续执行

SuspendResumeAbort均已标记为过时(Obsolete),实际开发中不应使用。线程无法真正从外部被强制终止,这些方法只是通过异常机制间接干预,存在安全隐患。

线程等待

方式一:观望式轮询(不推荐)

while (thread.ThreadState != ThreadState.Stopped) { Thread.Sleep(500); // 每 500ms 检查一次 }

❌ 持续轮询会浪费 CPU 时间片,不推荐。

方式二:Join 等待(推荐)

thread.Join(); // 无限等待,直到线程结束 thread.Join(500); // 最多等待 500ms,过时不候 thread.Join(TimeSpan.FromMilliseconds(500)); // 同上,TimeSpan 写法

Join()是最简洁直接的线程等待方式。

前台线程 vs 后台线程
thread.IsBackground = true; // 后台线程:程序关闭时,线程立即终止 thread.IsBackground = false; // 前台线程:程序关闭时,等待线程执行完毕再退出
  • 默认:通过new Thread()创建的线程是前台线程
  • ThreadPool中的线程全部是后台线程
  • 主线程(Main 方法所在线程)是前台线程

⚠️ 如果忘记将长时间运行的线程设为后台线程,可能导致程序关闭后进程仍然存活。

线程优先级
thread.Priority = ThreadPriority.Highest; // 最高优先级 thread.Priority = ThreadPriority.Lowest; // 最低优先级

⚠️ 设置优先级只是提高被优先调度的概率,操作系统不保证严格按优先级执行,不能依赖它来控制执行顺序。


三、Thread 扩展封装

问题背景

多线程天然无序,但有时需要:

  1. 两个操作都在新线程中执行
  2. 同时保证它们的执行顺序

方案一:顺序执行两个委托

把两个委托放进同一个线程,天然保证顺序:

private void CallBackThread(ThreadStart threadStart, Action action) { Thread thread = new Thread(() => { threadStart.Invoke(); // 先执行 action.Invoke(); // 后执行 }); thread.Start(); }

方案二:带返回值的异步执行

核心思路:立即开启新线程执行,返回一个"取结果的委托",调用方在需要结果时才触发等待。

private Func<T> CallBackFunc<T>(Func<T> func) { T result = default; Thread thread = new Thread(() => { result = func.Invoke(); // 新线程中执行 }); thread.Start(); // 立即开启,不阻塞 // 返回一个延迟取值的委托 return new Func<T>(() => { thread.Join(); // 调用时才等待线程完成 return result; }); }

使用示例:

Func<int> asyncFunc = CallBackFunc(() => { Thread.Sleep(5000); return DateTime.Now.Year; }); // 这里不阻塞,可以继续做其他事 Console.WriteLine("继续执行其他任务..."); // 需要结果时才等待 int result = asyncFunc.Invoke();

⚠️.NET CoreDelegate.BeginInvoke/EndInvoke已不再支持,不能用于异步执行委托,需要用上述方式或Task替代。


四、ThreadPool 线程池

为什么需要线程池

Thread的问题:

  • API 繁多,控制复杂
  • 线程数量需要程序员手动管理,容易滥用
  • 频繁创建/销毁线程,系统开销大

ThreadPool的优势:

  • .NET Framework 2.0引入,采用池化思想
  • 预先创建线程放入池中,需要时直接取用,用完自动归还
  • 自动管理线程数量,防止资源耗尽
  • API 更简洁,去掉了 Thread 中不必要的方法

申请线程执行任务

ThreadPool.QueueUserWorkItem(obj => { DoSomething(obj.ToString()); }, "参数");

线程等待:ManualResetEvent

ThreadPool 没有Join(),需要用ManualResetEvent实现等待。

ManualResetEvent resetEvent = new ManualResetEvent(false); // 初始为未触发 ThreadPool.QueueUserWorkItem(obj => { DoSomething(obj.ToString()); resetEvent.Set(); // 任务完成,发出信号 }, "Richard"); // 主线程可以继续做其他事 Console.WriteLine("主线程继续执行..."); resetEvent.WaitOne(); // 阻塞,直到 Set() 被调用 Console.WriteLine("子线程已完成");

工作原理:

  1. new ManualResetEvent(false)— 初始状态为"未触发"
  2. 子线程任务完成后调用Set()— 状态变为"已触发"
  3. 主线程调用WaitOne()— 阻塞等待,直到状态变为"已触发"才继续

控制线程数量

ThreadPool.SetMinThreads(4, 4); // 设置最小工作线程数和 IO 线程数 ThreadPool.SetMaxThreads(8, 8); // 设置最大工作线程数和 IO 线程数 ThreadPool.GetMinThreads(out int minWorker, out int minIO); ThreadPool.GetMaxThreads(out int maxWorker, out int maxIO);
  • 第一个参数:工作线程数(Worker Threads),用于 CPU 密集型任务
  • 第二个参数:IO 线程数(Completion Port Threads),用于 IO 操作

强烈不建议手动设置线程池大小。原因:

  • 这是进程级别的全局设置
  • TaskParallelasync/await底层都使用线程池,修改后会影响所有这些功能
  • 设置不当容易引发性能问题甚至死锁

五、Thread vs ThreadPool 对比

特性ThreadThreadPool
出现时间.NET 1.0.NET 2.0
线程管理手动创建和销毁自动管理,复用线程
API 复杂度复杂,功能多简单,易用
性能频繁创建销毁,开销大复用线程,性能好
线程等待Join()ManualResetEvent
默认线程类型前台线程后台线程
适用场景需要精细控制的长时间任务大量短时间并发任务

六、注意事项与最佳实践

✅ 短时间任务优先使用ThreadPool
✅ 使用Join()进行 Thread 的线程等待
✅ 使用ManualResetEvent进行 ThreadPool 的线程同步
✅ 长时间运行的线程记得设置IsBackground = true
❌ 避免使用Abort()Suspend()Resume()
❌ 不要手动设置线程池大小
❌ 不要在生产环境依赖线程优先级来控制执行顺序

⚠️ 多个线程访问共享资源时需要加锁(lockMonitor等),否则会出现数据竞争问题。
⚠️ 线程内部的异常不会自动传播到主线程,必须在线程内部自行处理。
⚠️ 在实际项目中,更推荐使用Taskasync/await,它们是对线程池的更高层封装,代码更简洁,异常处理和取消机制也更完善。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 15:01:27

前端自动化测试

前端自动化测试&#xff1a;提升效率与质量的关键 在当今快节奏的软件开发环境中&#xff0c;前端自动化测试已成为保障产品质量、提升开发效率的重要手段。随着前端技术的复杂化&#xff0c;手动测试已无法满足频繁迭代的需求&#xff0c;而自动化测试通过脚本执行重复性任务…

作者头像 李华
网站建设 2026/7/14 15:01:38

动力电池充电系统设计说明书

动力电池充电系统设计&#xff08;mtalab仿真proteus仿真 10055-动力电池充电系统设计&#xff08;mtalab仿真proteus仿真详细介绍说明书PPT&#xff09; 资料摘要&#xff1a; 现在科技水平发展巨大&#xff0c;并且新能源技术也得到了相应发展&#xff0c;随着油价的增高以及…

作者头像 李华
网站建设 2026/7/14 15:01:38

告别盲目采购!智能请购功能,让采购管理更省心

做实体生意、管中小工厂的老板和采购岗伙伴&#xff0c;大概率都被这两个问题反复困扰&#xff1a;要么原料库存见底&#xff0c;临时加急采购耽误生产、丢了订单&#xff1b;要么凭经验盲目囤货&#xff0c;大量资金压在滞销库存里&#xff0c;周转不开越做越累。 传统的采购模…

作者头像 李华
网站建设 2026/7/14 15:01:36

维赛免清洗防锈油|焊接前不用洗,防锈还能直接焊

随着汽车变速箱档位持续提升&#xff0c;结构设计日趋复杂&#xff0c;越来越多的薄壁材料和精密零件开始采用激光焊接工艺。相比传统焊接&#xff0c;激光焊接优势明显&#xff1a;热影响区小、焊接速度快、焊缝平整美观、易于实现自动化。但它的“高要求”也同样突出——对工…

作者头像 李华
网站建设 2026/7/14 15:01:39

714 买卖股票的最佳时机含手续费

题目 给定一个整数数组 prices&#xff0c;其中 prices[i]表示第 i 天的股票价格 &#xff1b;整数 fee 代表了交易股票的手续费用。 你可以无限次地完成交易&#xff0c;但是你每笔交易都需要付手续费。如果你已经购买了一个股票&#xff0c;在卖出它之前你就不能再继续购买股…

作者头像 李华
网站建设 2026/7/14 15:01:37

因信息缺失暂无法生成安全新闻标题

因资讯信息不足无法展开复盘由于提供的资讯仅为“403 错误&#xff0c;您请求的资源需要进行身份验证”&#xff0c;未包含安全事件爆发的完整时间线、攻击规模、受损资产、漏洞技术链路、企业安全问题、事件次生灾害等关键信息&#xff0c;所以无法按照要求完成对安全事件的复…

作者头像 李华