一、思维导图

以下是本周学习内容的整体知识脉络:


二、思维导图详解

1. 泛型

泛型的核心思想是类型参数化——用占位符 <T> 代替具体类型,让类、方法、接口可以适配任意数据类型,同时保持编译时的类型安全。大大避免了传统 ArrayList 的装箱拆箱开销。常见泛型有 List<T>Dictionary<TKey, TValue> 以及自定义泛型类/方法。

2. 文件操作

C# 操作文件主要有两类方式:

  • File 静态类File.ReadAllText / File.WriteAllText 一次性读写文本,简单直接,适合小文件。
  • 文件流FileStream 按字节流读写,StreamReader / StreamWriter 按字符流读写,适合逐行处理大文件,自动处理编码。

选择原则:小文件用 File,大文件用流

3. 委托

委托是 C# 中将方法作为数据类型传递的机制。从最早的手写 delegate 声明,到内置泛型委托 Func<T>(有返回值)、Action<T>(无返回值)、Predicate<T>(返回 bool),再到匿名方法和 Lambda 表达式,语法越来越简洁。委托是后面事件和 LINQ 的基础。

4. 事件

事件基于委托实现了发布-订阅模式。发布者通过 event 关键字声明事件,订阅者注册处理方法,事件触发时所有订阅者依次响应。标准做法是使用 EventHandler / EventHandler<TEventArgs> 委托。事件的核心价值在于解耦:发布者不需要知道谁来处理,订阅者可以随时增减。

5. 进程 (Process)

System.Diagnostics.Process 允许程序启动、监视、关闭外部进程

Process.Start("notepad.exe");         // 启动记事本
Process.Start("https://example.com");  // 打开网页

通过 Process.IdProcess.ExitCodeProcess.Kill() 等可以完整管理外部程序的生命周期。

6. 线程 (Thread)

线程是操作系统调度的最小单位,C# 通过 System.Threading.Thread 来管理:

  • 前台线程:应用程序必须等所有前台线程结束后才退出。
  • 后台线程 (IsBackground = true):应用程序退出时自动终止,不阻止进程关闭,常用于日志写入等非关键任务。
  • Join():阻塞当前线程,等待目标线程执行完毕,实现线程间协调。
  • Lock 锁:多线程同时访问共享资源时,lock 语句确保同一时刻只有一个线程进入临界区,解决线程安全问题
  • ThreadPool:复用线程,避免频繁创建销毁的开销,适合大量短任务。

三、最近遇到的问题与解题思路

问题:日志写入偶现"文件被占用"异常

用后台线程异步写日志,并在主线程退出前调用 Join 等待完成:

Thread logThread = new Thread(() =>
{
    File.AppendAllText("log.txt", message);
});
logThread.IsBackground = true;
logThread.Start();

发现两种场景有问题:其一,日志线程没写完主线程就退出了(忘了 Join);其二,多个请求同时写同一个日志文件,抛出 IOException

解题思路

  1. 后台线程 ≠ 随叫随停:即使设为 IsBackground = true,线程终止也是"突然死亡",中间恰好执行到写文件就会被截断。正确做法是在退出前 logThread.Join() 等待完成。

  2. 文件写入是共享资源:多个线程不能同时写同一个文件,在写文件外围加 lock

    private static readonly object _fileLock = new object();
    
    lock (_fileLock)
    {
        File.AppendAllText("log.txt", message);
    }
    

    这样同一时刻只有一个线程可以写入,彻底解决冲突。

关键认知:后台线程解决的是"不阻塞进程退出",Join 解决的是"确保任务完成"Lock 解决的是"多个线程不乱抢资源"

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐