C# 线程(Thread)

一、线程(Thread)介绍

1.1 线程的基本概念

        线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位 。
        在 C# 程序里,一个进程默认会有一个主线程,我们也可以手动创建多个子线程,让它们并行(或并发)执行不同任务,比如同时进行文件下载、数据处理、界面渲染等操作,提升程序整体运行效率。

1.2 线程的应用场景

  • 后台任务处理:像应用程序启动时加载配置文件、定期清理临时文件等,可通过后台线程执行,避免阻塞主线程,保证界面交互流畅。
  • 并行计算:处理大规模数据运算(如数组排序、图像像素处理)时,利用多线程并行计算,充分发挥 CPU 多核优势,缩短运算时间。
  • 网络通信:服务器程序中,为每个客户端连接创建独立线程处理请求,实现同时服务多个客户端;客户端也可通过线程异步发起网络请求,不影响其他操作。

1.3 线程的生命周期

        线程是轻量级进程。每个线程都定义了一个独特的控制流。线程的生命周期始于 System.Threading.Thread 类的对象创建,终于线程终止或执行完成。

        以下是线程生命周期中的各种状态 −

1.未启动状态

线程实例已创建,但 Start 方法尚未调用。

2.就绪状态

线程已准备好运行,正在等待 CPU 周期。

3.不可运行状态

  • 以下情况线程不可执行:

  • 调用了 Sleep 方法

  • 调用了 Wait 方法

  • 被 I/O 操作阻塞

4.死亡状态

线程完成时的状态执行或中止。


二、线程基础

2.1 线程的创建

        在 C# 中,可通过 System.Threading.Thread 类创建线程。示例如下:

static void Main()
    {
        // 创建无参数线程
        Thread thread1 = new Thread(DoWork);
        thread1.Start();

        // 创建带参数线程
        Thread thread2 = new Thread(DoWorkWithParam);
        thread2.Start("Hello, Thread!");

        Console.WriteLine("主线程继续执行...");
        Console.ReadLine();
    }

    static void DoWork()
    {
        Console.WriteLine("线程 1 执行任务");
    }

    static void DoWorkWithParam(object param)
    {
        Console.WriteLine($"线程 2 收到参数:{param},执行任务");
    }

        当然,我们也能够使用委托和lambda表达式的方法,来创建线程实例。示例如下

 static void Main(string[] args)
 {
     object obj = "Hello, Thread!";
     //第一种写法
     Thread thread = new Thread(DoWorkWithParam);
     thread.Start(obj);

     //第二种写法 delegate
     Thread thread1 = new Thread(new ParameterizedThreadStart(DoWorkWithParam));
     thread1.Start(obj);

     //第三种写法 lambda
     Thread thread2 = new Thread((arg) => { DoWorkWithParam(arg); });
     thread2.Start(obj);
 }

注意:

可传参的写法 注意参数类型必须为object.

2.2 线程的常用属性

以下是整理后的 Thread 常用属性速查表(C#)

Thread 常用属性速查表

属性名称类型作用
ApartmentStateApartmentState获取或设置线程的单元状态(STA/MTA/Unknown),影响 COM 交互。
CurrentCultureCultureInfo获取或设置当前线程的区域性,控制日期、数字等格式化规则。
CurrentPrincipalIPrincipal获取或设置线程的安全负责人,用于基于角色的权限检查。
CurrentThreadThread静态属性,获取当前正在执行的线程对象引用。
CurrentUICultureCultureInfo获取或设置资源管理器使用的区域性,影响本地化资源加载。
ExecutionContextExecutionContext获取线程的上下文信息(安全、调用逻辑等)。
IsAlivebool返回线程是否已启动且未终止(true/false)。
IsBackgroundbool获取或设置线程是否为后台线程(后台线程不影响进程退出)。
IsThreadPoolThreadbool返回线程是否属于托管线程池(true/false)。
ManagedThreadIdint获取托管线程的唯一标识符(只读)。
Namestring获取或设置线程名称(调试时标识线程)。
PriorityThreadPriority获取或设置线程调度优先级(Highest/Normal/Lowest 等)。
ThreadStateThreadState返回线程当前状态枚举(Running、WaitSleepJoin 等)。

部分属性使用示例如下:

Thread workerThread = new Thread(() => 
{
    Console.WriteLine("线程执行中...");
    Thread.Sleep(2000);
});
workerThread.Name = "后台工作线程"; // 设置线程名称
workerThread.Start();

Console.WriteLine($"线程名称: {workerThread.Name}");
Console.WriteLine($"线程状态: {workerThread.ThreadState}"); // 输出状态如Running/WaitSleepJoin

注意事项
线程的优先级并不是绝对的运行优先级,而是占比优先(概率大)


2.3 线程的常用方法

线程操作方法

方法名称作用描述注意事项
Thread.Start()启动线程的执行,使其进入就绪状态并等待CPU调度只能对未启动的线程调用,重复调用会抛出异常
Thread.Suspend()挂起线程执行(暂停),若线程已挂起则无效果.NET Core/.NET 5+已弃用,可能造成死锁或资源泄漏
Thread.Resume()恢复被挂起的线程.NET Core/.NET 5+已弃用,需与Suspend()配对使用
Thread.Interrupt()中断处于等待状态(Wait/Sleep/Join)的线程,抛出ThreadInterruptedException异常对运行状态的线程无效,需捕获异常处理
Thread.Join()阻塞当前线程,直到目标线程执行完毕可设置超时时间,避免永久阻塞
Thread.Sleep()使当前线程暂停指定毫秒数,释放CPU时间片静态方法,精度受系统时钟影响
Thread.Abort()强制终止线程(通过抛出ThreadAbortException),不保证立即停止.NET Core/.NET 5+已移除,可能破坏程序状态,已过时,慎用 

三、线程的内存原理

3.1 线程栈

        每个线程都有自己独立的线程栈,用于存储局部变量、方法参数、返回地址等临时数据 。栈的内存空间是连续的,由操作系统自动管理分配和释放。比如在一个线程的方法中定义局部变量 int num = 10;num 就会被存储在该线程的栈中。线程栈的大小一般是固定的(可在创建线程时设置,如 new Thread(...) { StackSize = 1024 * 100 }; ,单位为字节 ),若线程中局部变量过多、递归深度过大,可能导致栈溢出。

3.2 共享内存与线程同步

        
        进程的堆内存、静态变量等是线程间共享的 。多个线程同时访问、修改共享资源时,就会出现线程安全问题,比如多个线程同时对一个静态变量 count 进行自增操作或者是文件的操作时:

static int count = 0;
static void IncrementCount()
{
    for (int i = 0; i < 1000; i++)
    {
        count++; 
        // 实际是 count = count + 1,可能出现线程交错,导致结果不准确
    }
}

        这时需要通过线程同步机制(如锁 lockMonitorMutex 等 )来保证共享资源访问的原子性,避免数据竞争。

static object lockObj = new object();
static int count = 0;
static void IncrementCount()
{
    for (int i = 0; i < 1000; i++)
    {
        lock (lockObj)
        {
            count++; 
        }
    }
}

lock 内部借助 Monitor 实现,能确保同一时间只有一个线程进入临界区(lock 包裹的代码块 )访问共享资源。


四、总结

  1. 线程数量控制

    • CPU密集型任务:线程数 ≈ 处理器核心数

    • I/O密集型任务:可适当增加线程数

  2. 同步机制选择

    • 简单场景:lock关键字

    • 读写分离:ReaderWriterLockSlim

  3. 避免常见陷阱

    • 避免过度同步(锁粒度过粗)

    • 防止死锁(按固定顺序获取锁)

         C# 线程是实现多任务并发的关键工具,理解其基础概念、内存原理,熟练运用创建、控制线程的方法和属性,处理好线程同步问题,能让程序在性能、响应性上更出色 。但线程也并非越多越好,过多线程会带来线程上下文切换开销、资源竞争加剧等问题,实际开发中需结合场景合理设计线程模型,平衡性能与复杂度,让多线程编程真正成为提升程序质量的助力。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值