我们知道,在创建线程时,会用到pthread_create()函数 ,我们来简单介绍一下该函数:
pthread_create(线程的tid , 线程属性 , 工作函数名 , 函数需要的参数);
这篇博客要讲的线程属性,便是用于进行线程的初始化的,我们可以通过对线程属性的修改来自定义线程
目录
线程属性中的成员
线程的警戒缓冲区
线程的优先级指针
线程退出状态
线程栈地址
线程栈大小
修改线程属性的相关函数
修改线程属性的具体实现
接下来我们来了解一下什么线程属性
线程属性是一个结构体,用法为 pthread_attr_t 变量名(本篇博客里默认变量名为attr)
线程属性结构体中的成员分别有:线程的警戒缓冲区、线程的优先级指针、线程的退出状态、线程栈地址、线程栈大小
接下来,我们来介绍一下该结构体中的这几个成员
首先我们要知道,每当一个线程被创建出来的时候,都会有一个相应的线程栈出现,而栈存在溢出问题(栈的溢出都是上溢),线程栈是申请在堆空间的
一旦栈发生溢出,数据就会向上覆盖,影响甚至破坏到库、栈区等空间中的数据。但更可怕的是,由于用户对用户空间中的内容具有读写权限,线程栈上溢所导致的对这些内容的修改,系统是不会报错的,只有当数据溢出到内核层时,我们才能够发现问题,但此时已经晚了,数据已经全被破坏了,所以我们需要来给每个线程栈“加个盖子”,也就是所谓的“警戒缓冲区”。
警戒缓冲区的大小一般为4K,这块内存是不可读写的,所以当线程栈发生上溢,想要修改这块内存中的内容时,系统就会发现有线程非法操作内存,并杀死该线程,这样就可以保护其他内存中的数据
表示线程的优先级,一般情况下不建议修改,因为会影响系统的稳定性,一般只有杀毒软件或系统的防御软件才会修改其优先级
线程的退出状态有两种,分别是回收态(PTHREAD_JOINABLE)和分离态(PTHREAD_DETACH)
由于当修改线程属性时,线程还没有被创建,自然也就没有地址可存,所以默认情况下都是nil,表示空
线程栈大小一般情况下都是8M,但是我们知道,8M如果用二进制表示时非常大的数,将这么大的数放进去仅仅表示线程栈的大小其实没什么意义,所以默认情况下,这里存放的数据就是0,表示8M,申请空间时也是申请8M大小的空间
在了解了线程属性的组成之后,我们就要来了解一下修改线程属性的相关函数了
先介绍下一会会用到的几个变量:
函数 | 功能 | 返回值 |
pthread_attr_getdetachstate(&attr , &exit_state); | exit_state作为传出参数,可以获取线程属性中的退出状态 | 回收态返回 PTHREAD_CREATE_JOINABLE 分离态返回 PTHREAD_CREATE_DETACHED |
pthread_attr_setdetachstate(&attr , exit_state); | 通过传入参数exit_state,设置线程属性中的退出状态 | 成功返回0,失败返回非0错误码 |
pthread_attr_getstack(&attr , &thread_stack_addr , &thread_stack_size); | thread_stack_addr、thread_stack_size作为传出参数,可以获取线程属性中的栈地址与栈大小 | 返回两个参数——线程栈地址与线程栈大小 |
pthread_attr_setstack(&attr , thread_stack_addr , thread_stack_size); | 通过传入参数thread_stack_addr、thread_stack_size,可以设置线程属性中的栈地址与栈大小 | 成功返回0,失败返回非0错误码 |
pthread_attr_init(&attr); | 初始化线程属性结构体 | 成功返回0,失败返回-1 |
pthread_attr_destroy(&attr); | 释放线程属性结构体内存 | 成功返回0,失败返回-1 |
接下来,我们可以根据这些函数来实际操作一下,完成以下两个小任务
PS:64位机下,即使你修改了线程栈大小,创建的数目和原来还是一样的,因为你的修改是无效的,系统创建的线程栈大小还是8M,修改线程栈大小只有32位机有效
一、获取线程属性中默认的退出状态,以下是代码实现
#include
#include
#include
#include
#include
#include
#include
#include
int main()
{
pthread_attr_t attr;
//1.初始化线程属性
pthread_attr_init(&attr);
//2.检测线程属性中的线程退出状态
int detach_status;
pthread_attr_getdetachstate(&attr , &detach_status);
//3.判断是回收态还是分离态
if(detach_status == PTHREAD_CREATE_JOINABLE)
{
printf("线程属性默认为回收态\n");
}
else
{
printf("线程属性默认为分离态\n");
}
//4.释放线程属性结构体内存
pthread_attr_destroy(&attr);
printf("进程退出!\n");
}
怎么样,是不是很简单呢? 接下来,我们来完成第二个小任务
二、获取默认状态下线程属性中的线程栈地址与大小。将线程属性中的退出状态设置为分离态,修改线程栈地址,修改线程栈大小为1M,并测试使用这种线程属性的线程,系统一共能够创建多少个,以下是代码实现
//pthread_addr_change.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
void* thread_jobs(void* arg)
{
while(1)
{
sleep(1);
}
pthread_exit(NULL);
}
int main()
{
pthread_attr_t attr;
//1.初始化线程属性
pthread_attr_init(&attr);
//2.检测线程属性中的线程退出状态
int detach_status;
pthread_attr_getdetachstate(&attr , &detach_status);
//3.判断是回收态还是分离态并打印
if(detach_status == PTHREAD_CREATE_JOINABLE)
{
printf("线程属性默认为回收态\n");
}
else
{
printf("线程属性默认为分离态\n");
}
//4.将线程属性中的退出态修改为分离态
pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
//5.获取线程属性中线程栈的初始地址与大小并打印
void* thread_stack_addr;
size_t thread_stack_size;
pthread_attr_getstack(&attr , &thread_stack_addr , &thread_stack_size);
printf("线程栈地址为 %p , 线程栈大小为 %d \n" , thread_stack_addr , (int)thread_stack_size);
//6.通过malloc函数修改线程栈的初始地址,并将线程栈大小改为1M
pthread_t tid;
thread_stack_size = 0x100000;//0x100000代表1M
int flag = 0;
int errno;
while(1)
{
//如果malloc函数的返回值为NULL,就说明分配失败,内存已经用完
if((thread_stack_addr = (void*)malloc(thread_stack_size)) == NULL)
{
perror("thread_addr malloc failed!\n");
exit(0);//进程退出
}
//修改栈初始地址和大小
pthread_attr_setstack(&attr , thread_stack_addr , thread_stack_size);
//创建线程,并判断是否创建失败
if((errno = pthread_create(&tid , &attr , thread_jobs , NULL)) > 0)
{
perror("thread create failed!\n");
exit(0);//进程直接退出
}
else
{
flag++;
printf("flag = %d\n",flag);
}
}
pthread_attr_destroy(&attr);
return 0;
}
结果如下图所示:
以上就是本篇博客的全部内容了,大家有什么地方没有看懂的话,可以在评论区留言给我,咱要力所能及的话就帮大家解答解答
今天的学习记录到此结束啦,咱们下篇文章见,ByeBye!