深入理解Linux多线程:从概念到线程安全实践
发布时间:2026/8/18 12:36:17 作者:尧图编辑部 阅读量:1,286

1. 线程概念1.1 线程是什么线程是进程内部的一条执行流是CPU调度的基本单元。在Linux系统中线程的本质就是task_struct结构体。Linux下的调度单元就是PCB进程控制块而这个PCB相较于传统操作系统的PCB设计更加轻量因此Linux下的线程也被称作轻量级进程。1.2 进程与线程的区别功能性质上的差别进程是系统进行资源分配的基本单元线程是系统中进行CPU调度的基本单元使用上的差别针对多任务处理多进程稳定性更高多线程资源消耗成本低调度切换成本低线程间通信更加方便执行粒度更加细致1.3 线程间的共享与独有共享资源进程ID虚拟地址空间 页表打开的文件信息信号信息进程间通信描述信息......独有资源线程ID调度上下文信息线程栈空间......2. 线程控制2.1 Linux线程库Linux下的线程是通过pthread库POSIX线程库实现的这是一套封装好的库函数。pthread_create- 创建线程pthread_exit- 退出线程pthread_join- 等待线程pthread_detach- 分离线程2.2 用户态线程与内核态线程用户态线程针对线程上层封装描述后的执行流 数据的整体内核态线程指的是PCB-task_struct数据的整体 相对独有的数据2.3 线程IDpthread_t pthread_self()- 获取线程ID使用ps -L命令可以查看轻量级进程信息。注意轻量级进程IDLWP与代码中的pthread_t线程ID不是同一个东西轻量级进程ID就是task_struct里边的pid每个轻量级进程是唯一的pthread_t线程ID虚拟地址空间中线程独有线程描述空间的地址2.4 线程的退出方式在线程入口函数中return线程入口函数运行完毕线程就会退出pthread_exit(void* retval);- 在线程内部调用的任意函数中调用都可以退出线程void pthread_cancel(pthread_t tid);- 取消一个线程被取消的线程它的返回值是没有参考意义的是一个固定值PTHREAD_CANCELED2.5 线程的等待pthread_join(pthread_t tid, void** retval);- 等待指定线程退出获取它的返回值释放它的所有资源2.6 线程的分离void pthread_detach(pthread_t tid);- 设置线程的分离属性从joinable设置为detach状态重要特性线程一旦被分离线程退出后就会自动释放所有的资源不需要被等待也不能被等待。应用场景当我们不关心一个线程的返回值而且也不希望阻塞在等待上则使用线程分离。3. 线程安全3.1 线程安全的概念线程安全是指多个线程之间对同一份临界资源的访问的安全性要求。如何实现线程安全同步 互斥互斥的实现互斥锁同步的实现条件变量信号量3.2 函数的可重入与不可重入函数的重入多个线程在并发执行中同时进入同一个函数内部进行数据处理同时调用了同一个函数。可重入函数多线程重入函数后函数的执行结果并不会出现二义性。不可重入函数多线程重入函数后函数的执行结果并不确定。本质原因函数内部对一个临界资源进行了处理。3.3 互斥的实现 - 互斥锁互斥锁的本质原子操作的计数器0/1用于表示当前临界资源的访问状态是否正在访问中或者用于表示当前锁的状态加锁、解锁状态。通过这个状态表示当前临界资源是否可访问。互斥锁的使用步骤定义互斥锁pthread_mutex_t mutex;初始化互斥锁定义变量时直接初始化pthread_mutex_t mtx PTHREAD_MUTEX_INITIALIZER通过函数初始化pthread_mutex_init(pthread_mutex_t*, pthread_mutexattr_t*);属性通常置空使用互斥锁在访问临界资源之前加锁在访问临界资源完毕后解锁释放锁资源确保锁不会被使用之后释放锁资源加锁操作阻塞加锁int pthread_mutex_lock(pthread_mutex_t *mutex);- 阻塞加锁直到加锁成功后返回该加锁的阻塞有可能会被信号打断非阻塞加锁int pthread_mutex_trylock(pthread_mutex_t *mutex);- 非阻塞加锁解锁操作int pthread_mutex_unlock(pthread_mutex_t *mutex);释放锁int pthread_mutex_destroy(pthread_mutex_t *mutex);3.4 同步的实现 - 条件变量条件变量的本质进程的阻塞队列 让进程阻塞的接口 能够唤醒进程或线程的接口实现同步的原理当进程或线程在获取资源的时候如果不满足获取条件则调用阻塞接口让线程陷入阻塞。其他的线程促使资源获取的条件满足后通过调用唤醒接口唤醒那些阻塞的线程。条件变量的使用步骤定义条件变量pthread_cond_t初始化条件变量int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *restrict attr);pthread_cond_t cond PTHREAD_COND_INITIALIZER;阻塞线程当不满足资源获取条件时int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *restrict abstime);- 限时阻塞接口超时后阻塞也会被唤醒int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);唤醒阻塞的线程pthread_cond_signal(pthread_cond_t *cond);- 至少唤醒一个阻塞的线程pthread_cond_broadcast(pthread_cond_t *cond);- 唤醒所有阻塞的线程销毁条件变量pthread_cond_destroy(pthread_cond_t *cond);顾客-厨师模型流程顾客流程先加锁判断碗里有没有饭没有饭则陷入阻塞阻塞之前先解锁cond_wait中做了三件事1.解锁2.休眠3.加锁被唤醒之后先加锁重要注意事项判断当前若不满足条件则陷入阻塞这个过程应该是一个循环操作。目的被唤醒后加锁成功了则能再次判断资源获取条件是否满足。如果程序中存在多种不同的角色有多少种角色就应该定义多少个条件变量让不同的角色阻塞等待在不同条件变量的阻塞队列中避免出现唤醒角色错误的情况。4. 总结本文系统性地介绍了Linux多线程编程的核心概念包括线程的基本概念、线程控制、线程安全等关键知识点。通过理解线程与进程的区别、线程的创建与销毁、线程间的同步与互斥机制开发者可以编写出高效、安全的并发程序。在实际开发中需要根据具体场景合理选择线程同步机制确保程序的正确性和性能。