C# 多线程
参考
- 豆包
- DeepSeek
- 《C# 10 和 .NET 6 入门与跨平台开发(第6版)》
- https://learn.microsoft.com/zh-cn/dotnet/standard/threading/using-threads-and-threading
- https://learn.microsoft.com/zh-cn/dotnet/standard/threading/the-managed-thread-pool
- https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.threadpool?view=net-9.0
- https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.autoresetevent?view=net-9.0
- https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.thread?view=net-6.0
- https://blog.csdn.net/qq_31094099/article/details/80308960
- https://www.cnblogs.com/DonetRen/p/10167095.html
- https://www.cnblogs.com/yifengjianbai/p/5499493.html
- https://www.cnblogs.com/yifengjianbai/p/5463350.html
- https://www.cnblogs.com/yellow3gold/p/17264491.html
- https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.tasks.parallel?view=net-6.0
- https://www.cnblogs.com/woxpp/p/3925094.html
- https://learn.microsoft.com/zh-cn/dotnet/api/system.diagnostics.stopwatch.elapsedmilliseconds?view=net-8.0
- https://www.cnblogs.com/sparkdev/p/5906272.html
- https://www.pianshen.com/ask/94349591088/
- https://www.cnblogs.com/stulzq/p/16067610.html
环境
| 软件/系统 | 版本 | 说明 | 
|---|---|---|
| Windows | windows 10 专业版 22H2 64 位操作系统, 基于 x64 的处理器 | |
| Microsoft Visual Studio | Community 2022 (64 位) - Current 版本 17.13.6 | |
| .NET | 6.0 | 
正文
本文使用 DeepSeek 进行了相关内容的总结
完整示例代码
- Program.csusing System.ComponentModel; using System.Diagnostics; using System.Threading.Tasks; namespace ConsoleApp4 { public class Program { /// <summary> /// /// </summary> /// <param name="args"></param> static void Main(string[] args) { //TestThisThread(); //TestThreads(); //TestThreadPool(); //TestThreadPool1(); //TestTask(); //TestParallel(); TestBackgroundWorker(); } /// <summary> /// /// </summary> /// <param name="args"></param> //static async Task Main(string[] args) //{ // await TestTask1(); //} /// <summary> /// 获取当前主线程的状态 /// https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.thread?view=net-6.0 /// </summary> public static void TestThisThread() { // 以下属性均可以设置 Thread.CurrentThread.Name = "MainThread"; Console.WriteLine(Thread.CurrentThread.Name); Console.WriteLine(Thread.CurrentThread.CurrentCulture); Console.WriteLine(Thread.CurrentThread.IsAlive); Console.WriteLine(Thread.CurrentThread.IsBackground); Console.WriteLine(Thread.CurrentThread.IsThreadPoolThread); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); Console.WriteLine(Thread.CurrentThread.Priority); Console.WriteLine(Thread.CurrentThread.ThreadState); } /// <summary> /// 手动多个线程 /// https://learn.microsoft.com/zh-cn/dotnet/api/system.threading.thread?view=net-6.0 /// </summary> public static void TestThreads() { List<Thread> threads = new List<Thread>(); for (int i = 0; i < 10; i++) { Thread thread = new Thread(() => { Thread.CurrentThread.Name = "Thread"+i; Thread.Sleep(1000); Console.WriteLine(Thread.CurrentThread.Name); }); threads.Add(thread); } threads.ForEach(item => item.Start()); } /// <summary> /// 线程池 ThreadPool /// 主线程将在方法在线程池线程上运行之前退出。 线程池使用后台线程,如果所有前台线程都终止,后台线程不会使应用程序保持运行。 (这是一个争用条件的简单示例。) /// https://www.cnblogs.com/DonetRen/p/10167095.html /// </summary> public static void TestThreadPool() { //设置正在等待线程的事件为终止 //AutoResetEvent autoEvent = new AutoResetEvent(false); //获取处于活动状态的线程池请求的数目 ThreadPool.GetMaxThreads(out int workerThreads, out int portThreads); //设置辅助线程的最大数 workerThreads = 10; //设置线程池中异步I/O线程的最大数 portThreads = 500; //设置处于活动状态的线程池请求的数目 ThreadPool.SetMaxThreads(workerThreads, portThreads); //执行线程池 //ThreadPool.QueueUserWorkItem(new WaitCallback((object parame) => //{ // AutoResetEvent? stateInfo = parame as AutoResetEvent; // Thread.Sleep(1000); // Console.WriteLine(stateInfo?.ToString()); //}), autoEvent); for (int i = 0; i < 10; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback((object? index) => { Thread.Sleep(1000); Console.WriteLine("执行线程池"+ index?.ToString()); }), i.ToString()); } // 主线程将在方法在线程池线程上运行之前退出。 线程池使用后台线程,如果所有前台线程都终止,后台线程不会使应用程序保持运行。 (这是一个争用条件的简单示例。) Thread.Sleep(5000); } /// <summary> /// 线程池 ThreadPool1 /// 主线程将在方法在线程池线程上运行之前退出。 线程池使用后台线程,如果所有前台线程都终止,后台线程不会使应用程序保持运行。 (这是一个争用条件的简单示例。) /// https://www.cnblogs.com/DonetRen/p/10167095.html /// </summary> // 多线下原子操作操作 private static int _count = 100; public static void TestThreadPool1() { // 设置正在等待线程的事件为终止; 错误的使用(如忘记调用 Set())会导致线程永久阻塞(死锁) using AutoResetEvent autoEvent = new AutoResetEvent(false); //执行线程池 for (int i = 0; i < 100; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback((object? index) => { Thread.Sleep(1000); Console.WriteLine("执行线程池" + _count); // 原子操作 所有线程执行完毕后,释放锁,主线程WaitOne 处继续执行 // 将事件的状态设置为信号,允许一个或多个等待线程继续。 Interlocked.Decrement(ref _count); if(_count <= 0) { autoEvent.Set(); } }), i.ToString()); } // 阻止当前线程,直到当前 WaitHandle 收到信号。(等待完毕后,自动重置为 non-signaled。ManualResetEvent是手动复位) //autoEvent.WaitOne(); // 超时自动解锁,防止死锁 autoEvent.WaitOne(4000); } /// <summary> /// 线程 Task /// 主线程将在方法在线程池线程上运行之前退出。 线程池使用后台线程,如果所有前台线程都终止,后台线程不会使应用程序保持运行。 (这是一个争用条件的简单示例。) /// </summary> public static void TestTask() { Task task = new Task(() => { Thread.Sleep(1000); Console.WriteLine("task"); }); task.Start(); // var taskList = new List<Task>(); // TODO 将任务列表进行启动,或在初始化时就进行 new Task.Run // 等待任意一个任务完成 (支持设置等待超时时间,防止死锁) // await Task.WhenAny(taskList); // 等待所有任务完成 (支持设置等待超时时间,防止死锁) // Task.WaitAll(taskList.ToArray()); Console.WriteLine($"task.Status:{task.Status}"); Thread.Sleep(1000); Console.WriteLine($"task.Status:{task.Status}"); } /// <summary> /// 异步编程模型 Task /// 主线程将在方法在线程池线程上运行之前退出。 线程池使用后台线程,如果所有前台线程都终止,后台线程不会使应用程序保持运行。 (这是一个争用条件的简单示例。) /// </summary> public static async Task TestTask1() { await Task.Delay(1000); Console.WriteLine(nameof(TestTask1)); } /// <summary> /// 并行编程 Parallel /// </summary> public static void TestParallel() { Stopwatch swTask = new Stopwatch(); swTask.Start(); Parallel.For(0, 1000,(int i) => { Console.WriteLine(i); }); swTask.Stop(); Console.WriteLine("并行编程所耗时间:" + (swTask.ElapsedMilliseconds / 1000f)); swTask.Restart(); Parallel.Invoke(() => { Console.WriteLine(1); }, () => { Console.WriteLine(2); }, () => { Console.WriteLine(3); }, () => { Console.WriteLine(4); }, () => { Console.WriteLine(5); }); swTask.Stop(); Console.WriteLine("并行编程所耗时间:" + (swTask.ElapsedMilliseconds / 1000f)); swTask.Restart(); Parallel.ForEach(new List<int>() { 0,1,2,3,4 }, (int i) => { Console.WriteLine(i); }); swTask.Stop(); Console.WriteLine("并行编程所耗时间:" + (swTask.ElapsedMilliseconds / 1000f)); } /// <summary> /// 后台进程 BackgroundWorker 支持取消、支持异常处理 /// 主要用在 UI 界面操作上 /// </summary> public static void TestBackgroundWorker() { // 无参 BackgroundWorker backgroundWorker = new BackgroundWorker(); backgroundWorker.DoWork += (object? sender, DoWorkEventArgs e) => { Console.WriteLine(e.Argument); }; backgroundWorker.RunWorkerAsync(); // 传参 BackgroundWorker backgroundWorker1 = new BackgroundWorker(); backgroundWorker1.DoWork += (object? sender, DoWorkEventArgs e) => { Console.WriteLine(e.Argument); }; backgroundWorker1.RunWorkerAsync(100); // 传递给UI BackgroundWorker backgroundWorker2 = new BackgroundWorker(); backgroundWorker2.ProgressChanged += (object? sender, ProgressChangedEventArgs e) => { Console.WriteLine(e.ProgressPercentage); Console.WriteLine(e.UserState); }; backgroundWorker2.DoWork += (object? sender, DoWorkEventArgs e) => { BackgroundWorker? bgWorker = sender as BackgroundWorker; for (int i = 0; i < 100; i++) { // 不传递消息 // bgWorker?.ReportProgress(i); // 传递消息 bgWorker?.ReportProgress(i, "进度已更新"); Thread.Sleep(10); } }; // 设置WorkerReportsProgress=true才可以ReportProgress; https://www.pianshen.com/ask/94349591088/ backgroundWorker2.WorkerReportsProgress = true; backgroundWorker2.RunWorkerAsync(100); Thread.Sleep(5000); } } }
各方式优缺点分析
| 实现方式 | 优点 | 缺点 | 
|---|---|---|
| Thread | 1. 精细控制线程属性(优先级、名称) 2. 适合长期运行任务 | 1. 创建开销大(1MB/线程) 2. 需手动管理生命周期 3. 易出现闭包问题 | 
| ThreadPool | 1. 线程复用降低开销 2. 自动管理线程生命周期 3. 适合短时任务 | 1. 最大线程数有限(默认1023) 2. 无法控制具体线程 3. 后台线程不阻止进程退出 | 
| Task | 1. 支持async/await异步模型 2. 内置取消机制 3. 异常处理完善 | 1. 学习曲线较陡 2. 错误使用可能导致死锁(如.Result阻塞) | 
| Parallel | 1. 自动负载均衡 2. 简化数据并行操作 3. 适合CPU密集型循环 | 1. 不保证执行顺序 2. 共享资源需同步 3. 不适合I/O密集型操作 | 
| BackgroundWorker | 1. 内置进度报告 2. 自动跨线程更新UI 3. 支持取消操作 | 1. 主要适用于WinForms/WPF 2. 已被async/await替代 3. 控制台应用效果有限 | 
文章代码关键问题补充
- 
闭包问题( TestThreads方法)for (int i = 0; i < 10; i++) { Thread thread = new Thread(() => { // 这里i可能不是预期值(闭包捕获循环变量) Console.WriteLine("Thread" + i); }); }修复方案: for (int i = 0; i < 10; i++) { int local = i; // 创建局部副本 new Thread(() => Console.WriteLine("Thread" + local)).Start(); }
- 
线程同步机制( TestThreadPool1)- 使用 AutoResetEvent + Interlocked实现原子计数
- 改进建议:对于计数场景,推荐使用 CountdownEventvar countdown = new CountdownEvent(100); for (int i = 0; i < 100; i++) { ThreadPool.QueueUserWorkItem(_ => { // ... countdown.Signal(); }); } countdown.Wait(); // 等待所有完成
 
- 使用 
- 
现代替代方案( BackgroundWorker)// 使用Task替代BackgroundWorker var progress = new Progress<int>(percent => Console.WriteLine($"进度: {percent}%")); await Task.Run(() => { for (int i = 0; i < 100; i++) { ((IProgress<int>)progress).Report(i); Thread.Sleep(10); } });
示例代码未包含的重要多线程技术
- 
异步流(Async Streams) public async IAsyncEnumerable<int> GetAsyncStream() { for (int i = 0; i < 100; i++) { await Task.Delay(100); yield return i; } } await foreach (var item in GetAsyncStream()) { Console.WriteLine(item); }
- 
通道(System.Threading.Channels) var channel = Channel.CreateUnbounded<int>(); // 生产者 _ = Task.Run(async () => { for (int i = 0; i < 10; i++) { await channel.Writer.WriteAsync(i); } channel.Writer.Complete(); }); // 消费者 await foreach (var item in channel.Reader.ReadAllAsync()) { Console.WriteLine($"Received: {item}"); }
- 
并行LINQ(PLINQ) var results = data.AsParallel() .Where(x => x.IsValid) .Select(x => x.Process()) .ToList();
多线程最佳实践
- 
资源同步 - 使用 lock保护共享资源
- 使用线程安全集合(ConcurrentBag<T>,BlockingCollection<T>)
 
- 使用 
- 
取消机制 var cts = new CancellationTokenSource(); var token = cts.Token; Task.Run(() => { while (!token.IsCancellationRequested) { // 工作 } }, token); cts.CancelAfter(5000); // 5秒后取消
- 
异常处理 try { await Task.Run(() => RiskyOperation()); } catch (AggregateException ae) { ae.Handle(e => e is OperationCanceledException); }
线程选择指南(更新版)
| 场景 | 推荐方案 | 代码示例 | 
|---|---|---|
| UI响应式后台任务 | async/await + IProgress | await Task.Run(() => {...}, progress) | 
| 数据并行处理 | Parallel/PLINQ | Parallel.For(0, N, i => {...}) | 
| 短期后台任务 | ThreadPool.QueueUserWorkItem | ThreadPool.QueueUserWorkItem(WorkMethod) | 
| 长期独立任务 | Thread | new Thread(WorkMethod){IsBackground=true} | 
| 生产者-消费者模式 | Channels + async | Channel.CreateBounded<T>() | 
| 定时任务 | Timer + async | new Timer(async _ => await WorkAsync()) | 
重要提示:现代C#开发中,推荐优先使用 Task + async/await 组合,它覆盖了95%的并发场景,同时保持代码可读性和可维护性。保留 Thread 仅用于需要精细控制线程的特殊场景。
    博  主 :夏秋初
地 址 :https://www.cnblogs.com/xiaqiuchu/p/18915122
 
如果对你有帮助,可以点一下 推荐 或者 关注 吗?会让我的分享变得更有动力~
转载时请带上原文链接,谢谢。
    
地 址 :https://www.cnblogs.com/xiaqiuchu/p/18915122
如果对你有帮助,可以点一下 推荐 或者 关注 吗?会让我的分享变得更有动力~
转载时请带上原文链接,谢谢。
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号