.NET基础拾遗(5)多线程开发基础
.NET基础拾遗5多线程开发基础一、多线程概述为什么需要它在.NET开发中多线程是提升应用程序性能和响应能力的关键技术。想象一下一个桌面应用程序在下载文件时如果使用单线程UI会冻结直到下载完成——用户无法操作界面体验极差。多线程允许程序同时执行多个任务比如一个线程处理用户输入另一个线程在后台下载数据第三个线程更新进度条。在.NET中线程是操作系统调度的最小执行单元。每个进程至少有一个主线程而多线程编程就是创建和管理额外的线程来并行工作。但要注意线程不是越多越好——线程切换有开销且访问共享资源时可能引发竞态条件。因此理解基础概念和实践至关重要。### 二、基础概念Thread类与线程生命周期.NET中最基础的多线程类是System.Threading.Thread。它允许你创建、启动、暂停、恢复和终止线程。线程的生命周期包括未启动、运行、阻塞、休眠、终止等状态。#### 2.1 创建和启动线程以下代码展示如何用Thread类创建并启动一个简单线程csharpusing System;using System.Threading;class Program{ static void Main() { // 创建线程对象指定要执行的方法 Thread workerThread new Thread(DoWork); // 设置为后台线程应用程序退出时自动终止 workerThread.IsBackground true; // 启动线程 workerThread.Start(); // 主线程继续执行其他任务 Console.WriteLine(主线程正在运行...); // 等待用户输入防止控制台退出 Console.ReadLine(); } // 线程要执行的方法 static void DoWork() { for (int i 0; i 5; i) { Console.WriteLine($工作线程: 第 {i1} 次循环); // 模拟耗时操作让出CPU给其他线程 Thread.Sleep(500); } Console.WriteLine(工作线程完成。); }}输出示例主线程正在运行...工作线程: 第 1 次循环工作线程: 第 2 次循环工作线程: 第 3 次循环工作线程: 第 4 次循环工作线程: 第 5 次循环工作线程完成。注释说明-Thread.Sleep(500)让当前线程暂停500毫秒模拟耗时工作。-IsBackground true后台线程不会阻止进程退出。如果设为前台线程即使主线程结束进程也会等待该线程完成。- 注意主线程的Console.ReadLine()保证了控制台不会立即关闭否则后台线程可能被强行终止。#### 2.2 线程参数传递与返回值线程方法默认不能直接返回值但可以通过回调或共享变量实现。以下示例展示如何传递参数csharpusing System;using System.Threading;class Program{ static void Main() { // 使用ParameterizedThreadStart传递对象参数 Thread worker new Thread(CalculateSum); worker.Start(10); // 传递一个整数自动装箱为object // 另一种方式使用Lambda表达式推荐 Thread worker2 new Thread(() { int result 0; for (int i 1; i 5; i) result i; Console.WriteLine($Lambda线程计算结果: {result}); }); worker2.Start(); Console.ReadLine(); } // 带参数的方法参数必须是object类型 static void CalculateSum(object maxObj) { int max (int)maxObj; // 拆箱 int sum 0; for (int i 1; i max; i) sum i; Console.WriteLine($工作线程: 1到{max}的和是{sum}); }}输出示例工作线程: 1到10的和是55Lambda线程计算结果: 15注释说明-ParameterizedThreadStart允许传递一个object参数。但需要注意拆箱的类型安全。- Lambda表达式更简洁且可以捕获外部变量注意闭包陷阱是现代.NET多线程编程的首选方式。### 三、线程同步避免竞态条件当多个线程访问共享资源如全局变量、文件、数据库连接时必须同步以防止数据不一致。最常用的同步机制是lock语句。#### 3.1 无同步的竞态条件演示先看一个反面教材csharpusing System;using System.Threading;class Program{ static int counter 0; // 共享资源 static void Main() { // 创建两个线程同时增加counter Thread t1 new Thread(IncrementCounter); Thread t2 new Thread(IncrementCounter); t1.Start(); t2.Start(); // 等待两个线程完成 t1.Join(); t2.Join(); Console.WriteLine($最终结果: {counter} (期望: 200000)); Console.ReadLine(); } static void IncrementCounter() { for (int i 0; i 100000; i) { // 非原子操作读取、加1、写入 counter; } }}输出示例每次运行可能不同最终结果: 156823 (期望: 200000)原因分析counter不是原子操作它包含“读取-修改-写入”三步。当两个线程同时读取到相同值比如100各自加1后写入结果只增加了1而不是2。这就是竞态条件。#### 3.2 使用lock同步修复方法用lock对象保护临界区csharpusing System;using System.Threading;class Program{ static int counter 0; // 锁对象必须是引用类型且通常设为private static readonly private static readonly object lockObj new object(); static void Main() { Thread t1 new Thread(IncrementCounter); Thread t2 new Thread(IncrementCounter); t1.Start(); t2.Start(); t1.Join(); t2.Join(); Console.WriteLine($最终结果: {counter} (期望: 200000)); Console.ReadLine(); } static void IncrementCounter() { for (int i 0; i 100000; i) { // 进入锁只允许一个线程执行此代码块 lock (lockObj) { counter; } // 离开锁其他线程可以进入 } }}输出结果稳定最终结果: 200000 (期望: 200000)注释说明-lock (lockObj)确保同一时间只有一个线程能执行花括号内的代码。- 锁对象选择必须是所有相关线程都能访问的引用类型且不要用this、typeof或字符串字面量因为这些可能被其他代码意外锁定。- 性能影响锁会引入开销因此只保护必要的临界区。### 四、高级抽象Task与async/await从.NET 4.0开始System.Threading.Tasks.Task提供了更高级的并行编程模型。它基于线程池自动管理线程的创建和复用。配合async/awaitC# 5.0可以写出类似同步代码的异步程序。#### 4.1 Task基础使用csharpusing System;using System.Threading.Tasks;class Program{ static void Main() { // 创建并启动一个Task Task task1 Task.Run(() { for (int i 0; i 3; i) { Console.WriteLine($Task1 运行中... 迭代 {i1}); Task.Delay(300).Wait(); // 模拟异步等待 } }); // 创建返回值的Task Taskint task2 Task.Run(() { int sum 0; for (int i 1; i 100; i) sum i; return sum; }); // 等待所有任务完成 Task.WaitAll(task1, task2); // 获取task2的结果会阻塞直到完成 Console.WriteLine($Task2 结果: {task2.Result}); // 使用ContinueWith链式操作 Task.Run(() Hello) .ContinueWith(prev Console.WriteLine(prev.Result World)) .Wait(); Console.ReadLine(); }}输出示例Task1 运行中... 迭代 1Task1 运行中... 迭代 2Task1 运行中... 迭代 3Task2 结果: 5050Hello World注释说明-Task.Run在线程池上执行异步操作推荐用于CPU密集型任务。-Task.Delay非阻塞的异步等待相比Thread.Sleep不会占用线程。-Task.WaitAll阻塞当前线程直到所有任务完成。-ContinueWith在任务完成后执行后续操作类似回调。#### 4.2 async/await异步编程async/await让异步代码更易读特别适合I/O密集型操作如文件读写、网络请求csharpusing System;using System.Net.Http;using System.Threading.Tasks;class Program{ static async Task Main(string[] args) // Main也可以async { Console.WriteLine(开始下载...); string content await DownloadWebPageAsync(https://example.com); Console.WriteLine($下载完成内容长度: {content.Length} 字符); // 并发下载多个页面 Taskstring task1 DownloadWebPageAsync(https://httpbin.org/get); Taskstring task2 DownloadWebPageAsync(https://httpbin.org/ip); // 等待所有任务完成不阻塞UI线程 string[] results await Task.WhenAll(task1, task2); Console.WriteLine($第一个结果长度: {results[0].Length}); Console.WriteLine($第二个结果长度: {results[1].Length}); Console.ReadLine(); } static async Taskstring DownloadWebPageAsync(string url) { using (HttpClient client new HttpClient()) { // await 会释放当前线程但不会阻塞调用者 string response await client.GetStringAsync(url); return response; } }}输出示例开始下载...下载完成内容长度: 1256 字符第一个结果长度: 312第二个结果长度: 45注释说明-async标记方法await等待异步操作完成。- 在await执行时当前线程会返回线程池不占用资源操作完成后从线程池取回线程继续执行。-Task.WhenAll并发等待多个任务提高效率。- 注意Main方法也可以标记为async TaskC# 7.1但控制台应用仍需要ReadLine保持进程。### 五、总结多线程开发是.NET中提升性能和响应性的核心技能。从最基础的Thread类到线程同步的lock再到现代的Task和async/await我们经历了一条从底层到高层的演进路径1.Thread类提供最直接的控制但手动管理线程生命周期和同步容易出错。适合需要精细控制线程优先级的场景。2.lock同步解决竞态条件但过度使用会降低并发性能。应只保护最小的临界区。3.Task与async/await基于线程池自动管理线程复用async/await让异步代码逻辑清晰避免回调地狱。推荐用于大多数现代.NET应用。在实际开发中建议优先使用Task和async/await仅在需要低级别控制时使用Thread。同时牢记线程安全原则共享可变资源必须同步而不可变数据或线程本地存储则不需要。掌握这些基础你就能构建出高效、稳定的多线程应用。

相关新闻

如何在5分钟内体验AI情感识别?零配置启动的实时情绪分析系统

如何在5分钟内体验AI情感识别?零配置启动的实时情绪分析系统

如何在5分钟内体验AI情感识别?零配置启动的实时情绪分析系统 【免费下载链接】Emotion-recognition Real time emotion recognition 项目地址: https://gitcode.com/gh_mirrors/em/Emotion-recognition 你是否曾想过,让计算机读懂你的表情&#…

2026/10/18 19:45:20 阅读更多 →
游戏一般开帧,mmo等即时战斗的或者对流畅度有很高要求的可以开帧。 帧同步与状态同步的抉择。一般来说状态同步会比帧同步的前后端消息量 ...

游戏一般开帧,mmo等即时战斗的或者对流畅度有很高要求的可以开帧。 帧同步与状态同步的抉择。一般来说状态同步会比帧同步的前后端消息量 ...

游戏帧同步与状态同步的抉择:从开帧到消息量的深度解析 作为一名资深技术博主,今天我们来聊聊游戏开发中一个绕不开的话题——帧同步与状态同步的抉择。特别是对于MMO(大型多人在线)游戏、即时战斗类游戏或者对流畅度有很高要求的…

2026/10/18 23:28:38 阅读更多 →
基于OpenClaw与SeekDB构建智能知识库检索系统

基于OpenClaw与SeekDB构建智能知识库检索系统

1. 项目背景与核心价值 最近在整理个人知识库时,发现一个痛点:虽然用seekdb存储了大量技术笔记和参考资料,但随着数据量增长,检索效率越来越低。每次都要手动输入复杂查询条件,特别在跨多个标签搜索时尤为麻烦。于是萌…

2026/10/16 11:37:35 阅读更多 →

最新新闻

LSTM锂电池剩余寿命预测:Matlab实现与避坑指南

LSTM锂电池剩余寿命预测:Matlab实现与避坑指南

简介:这份Matlab源码包实现基于LSTM长短期记忆神经网络的锂电池剩余寿命预测,面向电池管理、健康管理及时序预测方向的工程师与研究人员,可直接用于NASA公开数据集上的剩余使用寿命(RUL)回归任务。资源共3个文件&#…

2026/10/18 23:28:30 阅读更多 →
ECC全解析:SAP ECC、纠错码与PFCG权限实战指南

ECC全解析:SAP ECC、纠错码与PFCG权限实战指南

1. ECC这三个字母,在不同圈子里到底指什么“ECC”这个缩写,放在不同技术社群里,指向的东西完全不是一回事。做企业信息化的朋友听到ECC,第一反应是SAP那套老牌ERP系统;搞嵌入式和存储的工程师听到ECC,脑子里…

2026/10/18 23:28:30 阅读更多 →
Git推送命令全解析:从基础操作到冲突处理与团队协作

Git推送命令全解析:从基础操作到冲突处理与团队协作

1. 推送命令的底层逻辑与核心价值1.1 为什么推送在 Git 里这么关键用过 Git 的人第一天就会接触git push,但很多人其实没有想明白:推送到底在做什么?简单说,git push是把本地仓库的提交记录,同步到远程仓库。它不是简单…

2026/10/18 23:28:30 阅读更多 →
git push 原理与实战:从快进推送到上游分支管理

git push 原理与实战:从快进推送到上游分支管理

你在终端里敲过多少次git push?可能多到数不清。但有没有想过:这条命令背后到底发生了什么?为什么有时候输入git push就静悄悄地推送成功,有时候却被远端一口回绝,提示rejected?为什么同事的新分支一行命令…

2026/10/18 23:28:30 阅读更多 →
我用AI深度搜索把模糊的会员需求拆成PRD,从两天缩到一天

我用AI深度搜索把模糊的会员需求拆成PRD,从两天缩到一天

我刚接手一个电商中台的会员体系需求时,业务方只丢过来一句话:“给高价值用户更多优惠和特权。”没了。当时正值季度末,开发排期已经定了一半,业务方下周就要评审。我对着空白的PRD文档坐了一整个下午,脑子里全是问题&…

2026/10/18 23:28:30 阅读更多 →
openclaw/workbuddy机器人绑定通道后只能默认自己使用问题解决:把settings改到TaoToken

openclaw/workbuddy机器人绑定通道后只能默认自己使用问题解决:把settings改到TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 23:27:29 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →