linux线程

线程共享的资源
- 文件描述符表
- 每种信号的处理方式
- 当前工作目录
- 用户ID和组ID
- 内存空间(除了栈区)
线程非共享资源
- 线程ID
- 函数运行上下文(各种寄存器的值),栈指针
- 栈空间
- errno变量
- 信号屏蔽字
- 调度优先级
线程常用操作
线程号:pthread_self()
#include <pthread.h>
pthread_t pthread_self();
- 功能:返回线程号
- 返回值:线程号
判断是否是一个线程: pthread_equal()
int pthread_equal(pthread_t t1, pthread_t t2);
- 功能:
判断线程号 t1 和 t2 是否相等。为了方便移植,尽量使用函数来比较线程 ID。 - 返回值:
相等:非 0
不相等:0
创建线程:pthread_creat()
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg );
- 参数:
thread
:线程标识符地址。
attr
:线程属性结构体地址,通常设置为 NULL。
start_routine
:线程函数的入口地址。
arg
:传给线程函数的参数。 - 返回值:
成功:0
失败:非 0
在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。
由于pthread_create的错误码不保存在errno中,因此不能直接用perror()打印错误信息,可以先用strerror()把错误码转换成错误信息再打印。
线程资源回收:pthread_join()
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
- 功能:
等待线程结束(此函数会阻塞),并回收线程资源,类似进程的 wait() 函数。 - 参数:
thread
:被等待的线程号。
retval
:用来存储线程退出状态的指针的地址。 - 返回值:
成功:0
失败:非 0
线程分离:pthread_detach()
#include <pthread.h>
int pthread_detach(pthread_t thread);
- 功能:
使调用线程与当前进程分离,分离后不代表此线程不依赖与当前进程,线程分离的目的是将线程资源的回收工作交由系统自动来完成,也就是说当被分离的线程结束之后,系统会自动回收它的资源。所以,此函数不会阻塞。 - 返回值:
成功:0
失败:非0
线程退出:pthread_exit()
#include <pthread.h>
void pthread_exit(void *retval);
- 功能:
退出调用线程。 - 参数:
retval:存储线程退出状态的指针。
线程取消:pthread_cancel()
#include <pthread.h>
int pthread_cancel(pthread_t thread);
- 功能:
杀死(取消)线程 - 参数:
thread : 目标线程ID。 - 返回值:
成功:0
失败:出错编号
注意:线程的取消有一定的延时。需要等待线程到达某个取消点(检查点)。类似于玩游戏存档,必须到达指定的场所(存档点,如:客栈、仓库、城里等)才能存储进度。
取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用creat,open,pause,close,read,write..... 执行命令man 7 pthreads可以查看具备这些取消点的系统调用列表。
可粗略认为一个系统调用(进入内核)即为一个取消点。
线程属性
typedef struct
{
int etachstate; //线程的分离状态
int schedpolicy; //线程调度策略
struct sched_param schedparam; //线程的调度参数
int inheritsched; //线程的继承性
int scope; //线程的作用域
size_t guardsize; //线程栈末尾的警戒缓冲区大小
int stackaddr_set; //线程的栈设置
void* stackaddr; //线程栈的位置
size_t stacksize; //线程栈的大小
} pthread_attr_t;
主要结构体成员:
-
线程分离状态
-
线程栈大小(默认平均分配)
-
线程栈警戒缓冲区大小(位于栈末尾)
-
线程栈最低地址
属性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init
,这个函数必须在pthread_create
函数之前调用。之后须用pthread_attr_destroy
函数来释放资源。
线程属性主要包括如下属性:作用域(scope)、栈尺寸(stack size)、栈地址(stack address)、优先级(priority)、分离的状态(detached state)、调度策略和参数(scheduling policy and parameters)。默认的属性为非绑定、非分离、缺省的堆栈、与父进程同样级别的优先级。
线程属性初始化和销毁
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
功能:
初始化线程属性函数,注意:应先初始化线程属性,再pthread_create创建线程
参数:
attr:线程属性结构体
返回值:
成功:0
失败:错误号
int pthread_attr_destroy(pthread_attr_t *attr);
功能:
销毁线程属性所占用的资源函数
参数:
attr:线程属性结构体
返回值:
成功:0
失败:错误号
线程分离状态
线程的分离状态决定一个线程以什么样的方式来终止自己。
- 非分离状态:线程的默认属性是非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join()函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。
- 分离状态:分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。应该根据自己的需要,选择适当的分离状态。
#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
功能:设置线程分离状态
参数:
attr:已初始化的线程属性
detachstate: 分离状态
PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD_CREATE_JOINABLE(非分离线程)
返回值:
成功:0
失败:非0
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
功能:获取线程分离状态
参数:
attr:已初始化的线程属性
detachstate: 分离状态
PTHREAD_CREATE_DETACHED(分离线程)
PTHREAD _CREATE_JOINABLE(非分离线程)
返回值:
成功:0
失败:非0
这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在pthread_create
函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create
的线程就得到了错误的线程号。
要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用pthread_cond_timedwait
函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create
返回。
设置一段等待时间,是在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
线程使用注意事项
-
主线程退出其他线程不退出,主线程应调用`pthread_exit
-
避免僵尸线程
pthread_join
pthread_detach
pthread_create
指定分离属性
被join线程可能在join函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;
-
malloc
和mmap
申请的内存可以被其他线程释放 -
应避免在多线程模型中调用fork,除非马上
exec
,子进程中只有调用fork的线程存在,其他线程t在子进程中均pthread_exit
-
信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)