Linux线程全面解析 📅 发布时间:2026/8/29 4:09:30 👁 浏览次数: 一、线程的概念与定位1.1 进程与线程的层次关系进程是操作系统资源分配的最小单位拥有独立的虚拟地址空间0~4GB、文件描述符表、信号处理等资源。线程是操作系统任务调度的最小单位是进程内的一个执行流。一个进程可以包含多个线程所有线程共享该进程的堆区、数据区、文本区及内核空间但每个线程拥有独立的栈区默认8MB和线程局部存储TLS。1.2 线程的轻量级特性相比进程线程具有以下突出优势维度进程线程创建开销需复制0~3GB用户空间耗时较长仅分配栈空间复用进程资源创建快切换开销需切换页表、刷新TLB开销大仅切换寄存器上下文开销小内存消耗独立地址空间内存占用大共享大部分内存并发量可更高安全性进程间地址隔离安全性高共享内存相互影响安全性较低通信方式需借助IPC管道、共享内存、套接字等可直接通过全局变量、队列等进行通信二、线程的创建与启动2.1pthread_create接口线程的创建通过pthread_create函数完成该函数属于POSIX线程库。函数原型#includepthread.hintpthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg);参数说明thread输出参数用于存储新创建线程的ID。attr线程属性对象传入NULL表示使用默认属性非分离、默认栈大小等。start_routine线程入口函数指针线程启动后即执行该函数。arg传递给入口函数的参数类型为void *可灵活传递任意类型数据。返回值成功返回0失败返回非零错误码如EAGAIN表示资源不足。2.2 编译与链接注意事项使用pthread接口时编译和链接阶段需显式链接线程库gcc-pthreadmy_program.c-omy_program-pthread选项同时启用线程相关的宏定义并链接libpthread。2.3 主线程与子线程的关系主线程即调用pthread_create的线程不能先于子线程退出否则进程结束将导致所有子线程被强制终止。因此主线程通常需要调用阻塞回收函数等待子线程结束。三、线程的退出与资源回收3.1 线程退出方式线程可通过以下方式退出执行线程入口函数执行return语句正常结束。线程主动调用pthread_exit(void *retval)退出retval为返回值指针。线程所在进程结束通过exit()或主线程return所有线程强制终止。被其他线程取消pthread_cancel。pthread_exit原型voidpthread_exit(void*retval);3.2 线程的分离属性与回收线程的回收机制取决于其分离属性非分离属性默认线程结束后其资源栈、寄存器上下文等不会被自动释放需要其他线程通常是创建者调用回收函数获取其退出状态并释放资源。若未回收将造成资源泄漏类似进程的僵尸态。分离属性线程设置为分离后其资源在退出时由系统自动回收无需其他线程干预。设置分离属性intpthread_detach(pthread_tthread);该函数将指定线程标记为分离状态调用后该线程不能被其他线程回收。3.3 阻塞回收pthread_join对于非分离线程使用pthread_join阻塞等待并回收资源。函数原型intpthread_join(pthread_tthread,void**retval);thread要等待的线程ID。retval输出参数存储线程退出时的返回值即pthread_exit或return传递的指针若不需要可为NULL。成功返回0失败返回错误码。调用pthread_join的线程将被阻塞直到目标线程结束。四、线程间通信与数据共享由于线程共享进程地址空间因此线程间通信非常直接全局变量所有线程可直接读写进程中的全局变量。动态分配的内存堆区通过malloc等分配的空间对所有线程可见。pthread_create参数传递可将数据地址作为arg传入线程入口函数。全局队列或环形缓冲区常用作生产者-消费者模型的数据通道。然而共享内存也带来了资源竞争风险必须引入同步互斥机制。五、线程互斥机制5.1 临界资源与临界区临界资源多个线程可同时访问的共享资源如全局变量、文件、数据库连接等。临界区访问临界资源的代码段。必须保证同一时刻只有一个线程进入临界区否则会导致数据不一致竞态条件。5.2 互斥锁Mutex互斥锁是实现互斥访问最常用的同步原语具有排他性任一时刻只能有一个线程持有锁。使用流程定义锁对象pthread_mutex_t mutex;初始化锁pthread_mutex_init(mutex, NULL);NULL表示默认属性在临界区入口加锁pthread_mutex_lock(mutex);阻塞直到获得锁在临界区出口解锁pthread_mutex_unlock(mutex);不再使用时销毁锁pthread_mutex_destroy(mutex);pthread_mutex_init原型intpthread_mutex_init(pthread_mutex_t*restrict mutex,constpthread_mutexattr_t*restrict attr);成功返回0失败返回错误码。5.3 其他锁类型读写锁pthread_rwlock_t允许多个线程同时读取但写操作独占。适合读多写少场景。自旋锁pthread_spinlock_t在等待锁时忙等待占用CPU而非阻塞睡眠适用于临界区极短且不希望线程切换开销的场景。六、线程同步机制6.1 同步的必要性同步用于控制线程之间的执行顺序使它们按照预期次序协作。典型场景包括生产者必须等消费者处理完缓冲区后才能继续生产或某任务需等待另一任务完成前置条件。6.2 信号量Semaphore信号量是经典的同步工具支持计数可用于控制同时访问某资源的线程数量或实现线程间的顺序协调。使用流程定义信号量对象sem_t sem;初始化sem_init(sem, 0, initial_value);pshared0表示线程间共享value为初始计数P操作申请sem_wait(sem);将信号量减1若结果为负则阻塞。V操作释放sem_post(sem);将信号量加1并唤醒等待线程。销毁sem_destroy(sem);sem_init原型intsem_init(sem_t*sem,intpshared,unsignedintvalue);pshared0表示线程间共享非0表示进程间共享需特殊设置。value初始资源计数。6.3 条件变量Condition Variable条件变量常与互斥锁配合用于线程在特定条件不满足时挂起直到条件满足被唤醒。典型接口包括pthread_cond_wait、pthread_cond_signal、pthread_cond_broadcast。七、死锁及其避免7.1 死锁的定义死锁是指多个线程或进程因竞争资源而陷入永久等待的状态每个线程都持有部分资源且等待其他线程占有的资源导致所有线程无法继续执行。7.2 常见死锁现象忘记释放锁加锁后未调用解锁。重复加锁同一线程对同一锁两次加锁若锁不支持递归。多锁抢占不当线程A持有锁1等待锁2线程B持有锁2等待锁1形成循环等待。7.3 死锁产生的四个必要条件根据Coffman条件死锁必须同时满足以下四点破坏任意一点即可防止死锁互斥条件资源独占一个时刻只能被一个线程使用。请求与保持条件线程已持有一部分资源同时请求新的资源且不释放已有资源。不剥夺条件已分配给线程的资源只能由该线程主动释放不能被强制剥夺。循环等待条件存在等待环路如T1→T2→T3→T1。7.4 避免死锁的实践策略锁操作成对出现加锁/解锁路径完全对应。所有线程按照相同的顺序获取多个锁破坏环路等待。使用超时加锁或非阻塞尝试如pthread_mutex_trylock和sem_trywait当无法获取锁时释放已持有的资源稍后重试。尽量减小锁粒度减少锁持有时间。使用RAII资源获取即初始化等技术自动管理锁生命周期仅C但C中可通过封装实现。八、线程调度概述Linux线程调度与进程调度共用同一调度器CFS——完全公平调度器遵循时间片轮询和优先级相结合的策略。调度行为宏观上表现为多任务并行多核系统中真正同时执行微观上为并发单核中分时切换。线程可设置调度策略SCHED_OTHER、SCHED_FIFO、SCHED_RR和优先级通过pthread_attr_setschedpolicy等属性接口调整。