📅 Day 18:多线程编程(Multithreading)
✅ 学习目标:
- 理解什么是线程(Thread) 和 多线程(Multithreading);
- 掌握如何在 C# 中创建和管理线程;
- 理解线程同步与资源共享问题(如竞态条件、死锁);
- 学会使用
Thread
类、Task
和Parallel
类进行并发编程; - 编写一个多线程下载器或计时器程序;
- 初步了解线程池和异步编程模型(为 Day 20 做铺垫)。
🧠 一、什么是线程?
线程(Thread) 是操作系统中最小的执行单元。一个进程(比如你的应用程序)可以包含多个线程,每个线程独立运行自己的任务。
多线程(Multithreading) 是指同时运行多个线程,以提高程序性能、响应速度和资源利用率。
🔁 二、C# 中的线程类:System.Threading.Thread
✅ 创建并启动线程
using System;
using System.Threading;class Program
{static void Main(){Thread thread = new Thread(DoWork);thread.Start(); // 启动新线程// 主线程继续执行for (int i = 0; i < 5; i++){Console.WriteLine("主线程输出:" + i);Thread.Sleep(500);}Console.WriteLine("主线程结束");}static void DoWork(){for (int i = 0; i < 5; i++){Console.WriteLine("工作线程输出:" + i);Thread.Sleep(600);}}
}
⚙️ 三、线程参数传递
你可以通过 ParameterizedThreadStart
或 Lambda 表达式传参:
Thread t = new Thread((obj) =>
{string name = (string)obj;Console.WriteLine("你好," + name);
});t.Start("小明");
🔒 四、线程同步与资源共享
当多个线程访问共享资源(如变量、文件)时,可能会出现 竞态条件(Race Condition),导致数据不一致。
示例:未同步的线程访问
static int count = 0;static void Increment()
{for (int i = 0; i < 100000; i++){count++;}
}// 多个线程同时调用 Increment() 可能导致 count 不等于预期值
✅ 使用 lock 锁定资源(推荐)
private static object lockObj = new object();static void SafeIncrement()
{for (int i = 0; i < 100000; i++){lock (lockObj){count++;}}
}
✅ 使用 Interlocked 实现原子操作(更高效)
static int count = 0;static void AtomicIncrement()
{for (int i = 0; i < 100000; i++){Interlocked.Increment(ref count);}
}
💡 五、线程优先级
你可以设置线程的优先级,影响调度顺序:
thread.Priority = ThreadPriority.High;
⚠️ 注意:高优先级并不保证先执行完,只是被调度的概率更高。
🧱 六、前台线程 vs 后台线程
默认情况下,所有线程都是前台线程(Foreground),它们会阻止程序退出直到执行完毕。
你可以将线程设为后台线程:
thread.IsBackground = true;
后台线程不会阻止程序退出。
🧩 七、线程池(ThreadPool)
线程池是一种高效的线程管理机制,避免频繁创建销毁线程。
ThreadPool.QueueUserWorkItem(state =>
{Console.WriteLine("这是线程池中的线程");
});
🧵 八、任务(Task) —— 更现代的并发方式
Task
是 .NET 提供的更高级的抽象,用于简化并发编程。
Task task = Task.Run(() =>
{Console.WriteLine("这是一个任务线程");
});
task.Wait(); // 等待任务完成
🔄 九、并行循环(Parallel Loops)
使用 Parallel.For
或 Parallel.ForEach
可以并行执行循环体:
Parallel.For(0, 10, i =>
{Console.WriteLine($"并行处理第 {i} 项,线程ID:{Thread.CurrentThread.ManagedThreadId}");
});
💪 实战练习:多线程下载器
功能要求:
- 用户输入多个 URL;
- 每个 URL 启动一个线程/任务下载网页内容;
- 下载完成后保存到本地文件;
- 显示每个下载所用时间。
示例代码结构:
using System;
using System.Net.Http;
using System.Threading.Tasks;class Program
{static async Task Main(){string[] urls = {"https://example.com","https://httpbin.org/get","https://jsonplaceholder.typicode.com/posts/1"};foreach (var url in urls){await DownloadAsync(url);}Console.WriteLine("所有下载完成!");}static async Task DownloadAsync(string url){using HttpClient client = new HttpClient();Console.WriteLine($"开始下载:{url}");string content = await client.GetStringAsync(url);string filename = $"download_{DateTime.Now.Ticks}.txt";await File.WriteAllTextAsync(filename, content);Console.WriteLine($"完成下载:{url},已保存为 {filename}");}
}
📝 小结
今天你学会了:
- 线程的基本概念及创建方法;
- 如何使用
Thread
类实现并发执行; - 掌握了线程同步技术(lock、Interlocked);
- 理解了前台线程与后台线程的区别;
- 学会使用
Task
和Parallel
实现更高效的并发编程; - 编写了一个多线程网页下载器示例。
多线程是提升程序性能和用户体验的重要工具,掌握它将使你在开发高性能应用方面游刃有余!
🧩 下一步学习方向(Day 19)
明天我们将进入一个新的主题 —— 异步编程(async / await),你将学会如何编写非阻塞的异步代码,提升 UI 应用的响应能力,以及优化网络请求和 I/O 操作。