我们知道,在创建线程时,会用到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!