09-C#.Net-多线程-基础篇-学习笔记

一、核心概念理解

1.1 进程与线程

进程(Process)

  • 计算机中的虚拟记录,描述应用程序运行时消耗的各种资源集合
  • 包含:CPU + 内存 + 磁盘IO + 网络资源
  • 类比:一个公司的整体运作

线程(Thread)

  • 程序进程中执行具体动作的最小执行流
  • 从点击按钮到网络通信,都是线程在执行
  • 类比:公司中的具体员工
  • 特点:线程依附于进程存在,进程消失则线程也消失

句柄(Handle)

  • long类型的数字,对应程序中的某个小部件
  • 是操作程序的最小单位标识

1.2 多线程的原理

CPU与多线程

  • 例如:6核12线程的CPU
  • 通过逻辑切分,将每个核心的执行时间分片
  • 每毫秒可执行的动作被切分成10000份

执行特点

  • 宏观:多个动作在一段时间内看似同时执行完毕(并发)
  • 微观:同一时刻只能处理一件事(串行)
  • 操作系统不断调度,动作A开始用某一分片,结束时可能用另一分片

二、同步与异步

2.1 同步方法(Sync)

特点

private void DoSomethingSync()
{
    // 线性执行,从上往下依次执行
    Console.WriteLine("步骤1");
    Thread.Sleep(1000);
    Console.WriteLine("步骤2");
    Thread.Sleep(1000);
    Console.WriteLine("步骤3");
}

优点

  • 符合人类思维逻辑,代码易读
  • 有序执行,结果可预测
  • 消耗计算机资源少

缺点

  • 执行慢,会卡顿界面
  • 只有一个线程(主线程/UI线程)参与计算
  • CPU忙碌时无暇处理其他任务

生活类比

  • 真心请人吃饭:Richard请Vn吃饭,Vn说还要忙一会儿,Richard说"那我等你,忙完一起去"

2.2 异步方法(Async)

特点

private void DoSomethingAsync()
{
    // 开启新线程执行
    Task.Run(() =>
    {
        Console.WriteLine("步骤1");
        Thread.Sleep(1000);
    });
    
    Task.Run(() =>
    {
        Console.WriteLine("步骤2");
        Thread.Sleep(1000);
    });
    
    // 主线程不等待,继续执行
    Console.WriteLine("主线程继续");
}

优点

  • 执行快,不卡顿界面
  • 多个线程并发执行
  • 提高程序响应能力

缺点

  • 无序执行,结果不可预测
  • 消耗更多计算机资源
  • 编程复杂度增加

生活类比

  • 客气请人吃饭:Richard客气地请Vn吃饭,Vn说还要忙一会儿,Richard说"那你先忙,我先去吃了"

核心理念

  • 以资源换时间:使用大量资源,降低时间成本

三、C#中的多线程实现

3.1 Thread(最原始)

Thread thread = new Thread(() =>
{
    Console.WriteLine("Thread执行");
});
thread.Start();
thread.IsBackground = true;  // 后台线程:进程结束,线程随之消失
thread.IsBackground = false; // 前台线程:进程结束,线程执行完才消失

3.2 ThreadPool(线程池)

  • 管理一组可重用的线程
  • 避免频繁创建和销毁线程的开销
// 将工作项加入线程池队列
ThreadPool.QueueUserWorkItem(state =>
{
    Console.WriteLine($"ThreadPool执行,线程ID: {Thread.CurrentThread.ManagedThreadId}");
});

// 带参数版本
ThreadPool.QueueUserWorkItem(state =>
{
    string msg = (string)state;
    Console.WriteLine($"参数: {msg}");
}, "hello");

⚠️ ThreadPool 无法精确控制线程,也不支持返回值和取消,实际开发中优先使用 Task。

3.3 Task(推荐使用)

Task的优势

  • .NET Framework 3.0时代实现
  • C#中多线程操作的最佳实现
  • 丰富的API,满足各种开发场景
  • Task操作的线程来自线程池

Task开启方式

// 方式1:new Task + Start
Task task = new Task(() =>
{
    Console.WriteLine("Task执行");
});
task.Start();

// 方式2:Task.Run(推荐)
Task.Run(() =>
{
    Console.WriteLine("Task执行");
});

// 方式3:TaskFactory
TaskFactory factory = new TaskFactory();
factory.StartNew(() =>
{
    Console.WriteLine("Task执行");
});

// 方式4:带返回值
Task<int> task = Task.Run(() =>
{
    return DateTime.Now.Year;
});

线程标识

// 通过线程ID确定是否为同一线程
int threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine($"当前线程ID: {threadId:00}");

四、Task的核心方法

4.1 等待方法

Wait() - 单个任务等待

Task task = Task.Run(() =>
{
    Thread.Sleep(3000);
    Console.WriteLine("任务完成");
});

task.Wait(); // 主线程等待,会卡顿界面
task.Wait(1000); // 限时等待,过时不候
task.Wait(TimeSpan.FromMilliseconds(1100)); // 使用TimeSpan

WaitAll() - 等待所有任务

List<Task> tasks = new List<Task>();
tasks.Add(Task.Run(() => DoWork1()));
tasks.Add(Task.Run(() => DoWork2()));
tasks.Add(Task.Run(() => DoWork3()));

Task.WaitAll(tasks.ToArray()); // 等待所有任务完成

WaitAny() - 等待任意一个任务

Task.WaitAny(tasks.ToArray()); // 任意一个任务完成即返回

4.2 回调方法(不阻塞)

ContinueWith() - 单个任务回调

Task task = Task.Run(() =>
{
    Thread.Sleep(3000);
    return "结果";
});

task.ContinueWith(t =>
{
    Console.WriteLine($"任务完成,结果:{t.Result}");
});

ContinueWhenAny() - 任意一个完成时回调

TaskFactory factory = new TaskFactory();
factory.ContinueWhenAny(tasks.ToArray(), completedTask =>
{
    Console.WriteLine("有一个任务完成了");
    // 不卡顿界面,用户体验好
});

ContinueWhenAll() - 所有完成时回调

factory.ContinueWhenAll(tasks.ToArray(), completedTasks =>
{
    Console.WriteLine("所有任务都完成了");
});

4.3 Delay() - 延迟执行

// Thread.Sleep - 阻塞主线程
Thread.Sleep(3000); // 主线程卡顿3秒

// Task.Delay - 不阻塞,配合ContinueWith使用
Task.Delay(3000).ContinueWith(t =>
{
    Console.WriteLine("3秒后执行");
    // 可能是新线程,也可能是主线程执行
});

五、应用场景

5.1 适合使用多线程的场景

场景1:多个独立任务并发执行

// 同时查询接口、缓存、数据库
List<Task> tasks = new List<Task>();
tasks.Add(Task.Run(() => QueryFromApi()));
tasks.Add(Task.Run(() => QueryFromCache()));
tasks.Add(Task.Run(() => QueryFromDatabase()));
Task.WaitAll(tasks.ToArray());

场景2:项目实战 - 多人协作开发

// 每个开发人员对应一个线程
TaskFactory factory = new TaskFactory();
List<Task> devTasks = new List<Task>();

devTasks.Add(factory.StartNew(() => Coding("张三", "权限模块")));
devTasks.Add(factory.StartNew(() => Coding("李四", "支付模块")));
devTasks.Add(factory.StartNew(() => Coding("王五", "订单模块")));

// 任意一人完成,准备环境
factory.ContinueWhenAny(devTasks.ToArray(), t =>
{
    Console.WriteLine("有人完成了,开始准备环境");
});

// 所有人完成,庆祝
factory.ContinueWhenAll(devTasks.ToArray(), t =>
{
    Console.WriteLine("所有人完成,一起吃饭庆祝");
});

5.2 实际业务场景

场景1:数据查询优先级

  • 同时查询缓存、数据库、第三方接口
  • 使用WaitAny,哪个先返回数据就用哪个
  • 其他线程自动放弃

场景2:首页数据加载

  • 首页包含:考勤信息、年度Top10、季度Top10、公告、通知
  • 每个模块开启一个线程并发查询
  • 使用WaitAll等待所有数据,组装后返回前端
  • 提高查询性能

5.3 不适合使用多线程的场景

  • 单个数据库查询(只有一个动作)
  • 简单的顺序操作
  • 需要严格顺序的业务逻辑

六、父子线程

Task parentTask = new Task(() =>
{
    Console.WriteLine("父线程开始");
    
    Task childTask1 = new Task(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("子线程1");
    });
    
    Task childTask2 = new Task(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("子线程2");
    });
    
    childTask1.Start();
    childTask2.Start();
    
    // 如果不等待子线程,父线程可能先结束
    // childTask1.Wait();
    // childTask2.Wait();
    
    Console.WriteLine("父线程结束");
});

parentTask.Start();
parentTask.Wait(); // 主线程等待父线程

注意事项

  • 父线程等待时,子线程可能还未执行完
  • 需要在父线程中显式等待子线程
  • 否则父线程结束,子线程内容可能未执行完

七、实用技巧

7.1 线程ID格式化

// 格式化为两位数,方便查看
string threadId = Thread.CurrentThread.ManagedThreadId.ToString("00");
Console.WriteLine($"线程ID: {threadId}");

7.2 时间戳格式化

string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
Console.WriteLine($"时间: {timestamp}");

7.3 性能测试

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

// 执行任务
DoSomething();

stopwatch.Stop();
Console.WriteLine($"耗时: {stopwatch.ElapsedMilliseconds}ms");

八、小结

  • 同步方法:有序、慢、阻塞、资源消耗少
  • 异步方法:无序、快、不阻塞、资源消耗多
  • Task 是首选:功能强大、API 丰富、来自线程池
  • Wait 系列:阻塞主线程,会卡顿界面
  • Continue 系列:不阻塞,用户体验好
  • 合理使用多线程:并发场景才使用,不要滥用
posted @ 2026-03-19 17:21  龙猫•ᴥ•  阅读(1)  评论(0)    收藏  举报