Linux 线程通信 学习笔记
Linux线程本质•在Linux内核中没有独立的“线程”数据结构线程的本质是“共享资源的轻量级进程(LightweightProcess简称LWP)”•内核视角每个线程对应一个“任务控制块(task_struct)”内核通过task_struct管理调度单元•资源共享多个LWP(线程)通过共享同一“进程地址空间(mm_struct)、文件描述符(files_struct)、信号处理表(signal_struct)”等核心资源实现“线程共享进程资源”的特性•与传统进程的区别普通进程的task_struct对应独立的mm_struct(独立地址空间)而线程的task_struct共享同一mm_struct本质上是“同一进程地址空间下的多个调度单元”线程的状态在Linux系统中线程作为内核调度的基本单元(轻量级进程)会因资源占用、执行阶段不同呈现多种状态•运行状态(R)也称之为可运行状态就是处于运行状态的线程正在CPU执行或等待CPU调度•可中断睡眠状态(S)线程在等待某个事件(如等待I/O操作、等待特定信号等)完成可被信号唤醒•不可以中断睡眠状态(D)线程正在等待关键硬件操作完成不能被信号中断确保数据完整性•停止状态(T)线程被暂停执行通常是收到了特定的信号(如SIGSTOP(暂停线程))或者在调试程序时被调试器暂停可被信号恢复执行(如SIGCONT)•僵尸状态(Z)线程已终止相关资源未被回收等待所属进程(或其他负责线程资源回收的机制)进行回收•死亡态(X)线程彻底终止资源全部释放是一个瞬间状态几乎无法观测到Linux线程与POSIX标准的兼容•标准遵循Linux 原生 POSIX 线程库libpthread完全遵循 POSIX 线程标准提供与其他 UNIX-like系统一致的线程接口确保多平台代码可移植性•核心接口与库依赖线程创建pthread_create、线程等待pthread_join等核心操作均由libpthread.so 库提供•编码时需包含头文件#include pthread.h声明库中函数接口•编译时需显式链接线程库gcc 源码.c -o 可执行文件 -lpthread否则会报 “未定义引用” 错误•内核交互方式Linux 下操作线程需通过 libpthread 库的 POSIX 接口而非直接调用内核系统调用用户态代码通过库封装与内核交互进程与线程的核心区别对比维度进程线程资源独立性拥有独立的内存空间、文件描述符等系统资源共享所属进程的内存空间和资源仅拥有独立的栈、程序计数器等创建/切换开销开销大(涉及资源分配与回收)开销小(仅需保存少量上下文信息)通信方式需通过进程间通信(IPC)机制(如信号、消息队列等等)可直接通过共享内存通信(需同步机制避免冲突)健壮性一个进程崩溃通常不影响其他进程一个线程崩溃可能导致整个进程崩溃适用场景任务间需要严格隔离(如浏览器标签页); 充分利用多核CPU处理CPU密集型任务(如大规模计算); 长时间运行的独立任务(如后台备份、视频转码); 运行不稳定或第三方代码(避免崩溃影响整体);任务间需要频繁共享数据(如Web服务器处理多请求); 轻量级并发追求低开销(如GUI程序后台加载数据); I/O密集型任务(如网络请求、文件读写可利用等待时间切换); 短期、协作紧密的子任务(如数据分片处理后汇总);线程创建与终止函数pthread_create函数项目内容所需头文件#include pthread.h原型int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);功能在调用进程中创建一个新的线程参数thread用于存储新创建线程的唯一标识符线程IDattr用于指定新线程的属性若为NULL则使用系统默认属性start_routine指向线程的入口函数arg用于给线程入口函数传递参数若无需参数可设为NULL返回值- 成功返回0新线程ID会写入thread指向的变量中- 失败返回非0错误码pthread_exit函数项目内容所需头文件#include pthread.h原型void pthread_exit(void *retval);功能结束当前线程参数retval用来给回收资源的线程传递线程的退出状态值若不想返回退出状态值可以传NULL返回值无线程标识函数pthread_self函数项目内容所需头文件#include pthread.h函数原型pthread_t pthread_self(void);功能获取调用线程的线程ID参数无返回值成功总是成功返回调用线程的线程ID线程回收函数pthread_join函数项目内容所需头文件#include pthread.h原型int pthread_join(pthread_t thread, void **retval);功能阻塞等待指定的已终止线程结束并为其回收资源参数thread要回收资源的线程IDretval用来接收线程退出的状态值不关心则传NULL返回值成功返回0失败返回非零错误码资源回收管理模式•线程创建后默认处于结合态若未显示设置为分离态其终止后资源不会立即释放必须由其他线程(通常是主线程)通过pthread_join( )主动回收•分离态的线程终止后会由系统自动回收资源无需其他线程调用pthread_join( );对比维度结合态默认分离态资源回收方式必须通过其他线程调用pthread_join()回收线程终止后系统自动回收资源pthread_join()作用有效调用后阻塞直到目标线程终止并回收其资源无效调用会返回错误无法等待分离态线程未回收的风险若主线程未调用pthread_join()线程终止后会成为“僵尸线程”占用task_struct等资源无风险终止后资源立即释放不会产生僵尸线程状态设置时机/方式创建时默认无需额外操作也可通过pthread_detach()函数从结合态转为分离态在pthread_create()的attr参数中设置PTHREAD_CREATE_DETACHED适用场景需要获取线程退出状态的场景无需获取线程退出状态、希望自动释放资源的场景pthread_attr_init函数项目内容所需头文件#include pthread.h原型int pthread_attr_init(pthread_attr_t *attr);功能将一个线程属性对象初始化为默认值参数attr需要被初始化的线程属性对象返回值成功返回0失败返回非0错误码pthread_attr_setdetachstate函数项目内容所需头文件#include pthread.h原型int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);功能设置线程属性对象的分离状态参数attr设置分离状态的线程属性对象detachstate用于指定线程的分离状态-PTHREAD_CREATE_JOINABLE表示将线程设置为结合态-PTHREAD_CREATE_DETACHED表示将线程设置为分离态返回值成功返回0失败返回非0错误码pthread_attr_destroy函数项目说明所需头文件#include pthread.h原型int pthread_attr_destroy(pthread_attr_t *attr);功能销毁一个已初始化的线程属性对象参数attr指定需要被销毁的线程属性对象返回值成功返回 0失败返回非 0 错误码线程控制函数pthread_detach函数项目内容所需头文件#include pthread.h原型int pthread_detach(pthread_t thread);功能标记一个线程为分离态参数thread线程id返回值成功返回0失败返回非0错误码pthread_cancel函数项目说明所需头文件#include pthread.h原型int pthread_cancel(pthread_t thread);功能请求取消指定的线程。目标线程对取消请求的处理取决于其取消状态和取消类型。参数thread要取消的线程ID返回值成功返回0失败返回非0错误码pthread_setcancelstate函数项目内容所需头文件#include pthread.h原型int pthread_setcancelstate(int state, int *oldstate);功能设置调用线程的可取消状态可取消状态PTHREAD_CANCEL_ENABLE可被取消默认PTHREAD_CANCEL_DISABLE不可被取消参数state新的可取消状态oldstate用于保存旧的可取消状态可为NULL返回值成功返回 0失败返回非 0 错误码pthread_setcanceltype函数项目内容头文件#include pthread.h原型int pthread_setcanceltype(int type, int *oldtype);功能设置调用线程的可取消类型可取消类型-PTHREAD_CANCEL_DEFERRED延时取消默认线程直到下一次调用可作为取消点的函数时才被取消。-PTHREAD_CANCEL_ASYNCHRONOUS异步取消线程可被立即取消。参数type要设置的新的可取消类型值。oldtype存储旧的可取消类型值。返回值- 成功返回0。- 失败返回非0的错误码。互斥锁•互斥锁(Mutex,即Mutual Exclusion Lock)是并发编程中用于保护 “共享资源” 的同步工具本质是一把“独占钥匙”•为什么需要互斥锁•当多个线程同时读写共享资源时会出现 “竞态条件”导致数据错误•互斥锁能强制线程 “排队访问” 资源避免竞争•核心作用保证 “同一时刻只有一个线程能持有锁并访问共享资源”实现 “互斥访问”pthread_mutex_init函数分类内容所需头文件#include pthread.h原型int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);功能动态初始化互斥锁参数mutex指定要进行初始化的互斥锁变量attr设置互斥锁的属性若传NULL使用系统默认属性pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;静态初始化返回值成功返回 0失败返回非 0 错误码pthread_mutex_lock函数项目内容所需头文件#include pthread.h原型int pthread_mutex_lock(pthread_mutex_t *mutex);功能用于获取互斥锁。当互斥锁已被其他线程获取时调用该函数的线程会被阻塞直到互斥锁可用并成功获取。参数mutex互斥锁返回值成功返回0失败返回非0错误码pthread_mutex_trylock函数项目内容所需头文件#include pthread.h原型int pthread_mutex_trylock(pthread_mutex_t *mutex);功能用于获取互斥锁当互斥锁已被其他线程获取时调用该函数的线程不会被阻塞而是立即返回错误。参数mutex互斥锁返回值成功返回0失败返回非0错误码pthread_mutex_unlock函数项目说明所需头文件#include pthread.h原型int pthread_mutex_unlock(pthread_mutex_t *mutex);功能解锁一般使用时只有加锁的线程可以解锁参数mutex互斥锁返回值成功返回 0失败返回非 0 错误码pthread_mutex_destroy函数项目内容所需头文件#include pthread.h原型int pthread_mutex_destroy(pthread_mutex_t *mutex);功能销毁互斥锁参数mutex互斥锁返回值成功返回0失败返回非0错误码条件变量•条件变量是并发编程中用于实现线程间“等待-通知”机制的同步工具可让线程在某个条件不满足时挂起待条件满足后被唤醒•为什么需要条件变量•仅用互斥锁能保证共享资源访问的互斥性但无法解决 “线程需等待特定条件成立才能继续执行” 的场景(比如生产者 - 消费者模型中消费者需等待生产者生产出数据才能消费)•条件变量可结合互斥锁让线程在条件不满足时主动释放锁并等待条件满足时被唤醒以重新获取锁继续执行灵活应对依赖条件的同步需求•核心作用实现线程间的 “条件等待与通知”使线程能在特定条件下协作避免线程忙等情况消耗 CPU资源提升并发效率pthread_cond_init函数项目内容所需头文件#include pthread.h原型int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);功能动态初始化条件变量参数cond指定要初始化的条件变量attr设置条件变量的属性若为NULL则使用系统默认属性pthread_cond_t cond PTHREAD_COND_INITIALIZER;静态初始化返回值成功返回0失败返回非0错误码pthread_cond_signal函数项目内容所需头文件#include pthread.h原型int pthread_cond_signal(pthread_cond_t *cond);功能唤醒至少一个等待在指定条件变量cond上的线程。参数cond指向要操作的条件变量。返回值成功返回0失败返回非0的错误码该错误码用于指示错误类型。pthread_cond_broadcast函数项目内容所需头文件#include pthread.h原型int pthread_cond_broadcast(pthread_cond_t *cond);功能唤醒所有等待在指定条件变量cond上的线程参数cond指定要操作的条件变量返回值成功返回0失败返回非0错误码pthread_cond_wait函数项目内容所需头文件#include pthread.h原型int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);功能线程阻塞在条件变量cond上等待被唤醒同时该函数会自动释放传入的互斥锁mutex当线程被唤醒时又会自动重新获取该互斥锁保证了线程在等待条件过程中互斥锁的正确管理避免死锁等问题。参数cond用于指定线程等待的条件变量mutex与条件变量配合使用保证线程在等待条件时的互斥返回值成功返回 0失败返回非 0 错误码pthread_cond_destroy函数项目内容所需头文件#include pthread.h原型int pthread_cond_destroy(pthread_cond_t *cond);功能销毁条件变量相关的资源参数cond要销毁的条件变量返回值成功返回0失败返回非0错误码无名信号量•无名信号量是一种用于线程同步或进程同步的工具通过维护一个计数值来控制对共享资源的访问可限制同时访问资源的线程或进程数量•为什么需要无名信号量•互斥锁只能保证同一时刻一个线程访问资源而有些场景需要允许多个线程同时访问•无名信号量的计数值可灵活设置能实现 “多线程有限并发访问” 或 “线程间的计数同步”补充互斥锁在多资源并发场景的不足•核心作用通过计数值控制共享资源的并发访问数量支持多线程在一定限制下同时访问资源或实现线程间基于计数的同步协作sem_init函数项目内容所需头文件#include semaphore.h原型int sem_init(sem_t *sem, int pshared, unsigned int value);功能初始化无名信号量参数sem要初始化的信号量指针pshared-0线程间同步同一进程内多个线程-≠0进程间同步需将信号量放在共享内存中value信号量初始计数值可用资源数量-1互斥锁一次允许一个线程/进程-0初始无资源调用sem_wait会阻塞返回值成功返回0失败返回-1并设置errnosem_wait函数项目内容所需头文件#include semaphore.h原型int sem_wait(sem_t *sem);功能信号量的值减 1。• 如果信号量的当前值大于 0减 1 后函数直接返回表示“获取资源成功”。• 如果信号量的当前值等于 0sem_wait会阻塞直到有其他线程/进程调用sem_post函数使得信号量值大于 0 后再执行“减 1”并返回。参数sem无名信号量返回值成功返回 0失败返回 -1并设置错误码不会改变信号量的值sem_post函数项目内容所需头文件#include semaphore.h原型int sem_post(sem_t *sem);功能释放信号量将信号量的值 1参数sem用于指定对哪个信号量进行释放操作返回值成功返回 0失败返回 -1并设置错误码不会改变信号量的值sem_destroy函数项目内容所需头文件#include semaphore.h原型int sem_destroy(sem_t *sem);功能销毁无名信号量释放该信号量相关的资源参数sem指定对哪个信号量进行销毁操作返回值成功返回0失败返回-1并设置错误码线程的互斥与同步应用场景工具应用场景特点优势局限性互斥锁单一共享资源同一时刻只允许一个线程访问主要用于防止数据竞争和不一致性。实现简单易于理解和使用能有效解决单一资源的互斥访问问题保障数据的一致性和完整性。若线程长时间持有锁可能导致其他线程长时间等待降低系统并发性能使用不当容易产生死锁例如多个线程循环等待对方持有的锁。条件变量线程需要在某个条件满足时才能继续执行配合互斥锁实现线程间的等待-通知机制。避免线程的忙等待有效节省 CPU 资源提高程序运行效率可以实现更灵活的线程同步逻辑满足复杂的条件等待和协作需求。必须与互斥锁配合使用条件的判断和通知的时机需要准确把握否则可能导致线程无法正确唤醒或错过唤醒影响程序正确性。无名信号量限制对共享资源的并发访问数量可用于线程或进程的同步和计数控制。可以灵活控制并发访问的资源数量适用于需要对资源进行定量管理的场景不仅能实现互斥还能进行计数同步功能比互斥锁更丰富。信号量的计数管理需要准确设置和维护容易出现资源过度使用或浪费的情况。