• 【Linux】线程属性的定义&如何修改线程属性(附图解与代码实现)


    我们知道,在创建线程时,会用到pthread_create()函数 ,我们来简单介绍一下该函数:

    pthread_create(线程的tid , 线程属性 , 工作函数名 , 函数需要的参数);

    这篇博客要讲的线程属性,便是用于进行线程的初始化的,我们可以通过对线程属性的修改来自定义线程

    目录

    线程属性中的成员

    线程的警戒缓冲区

    线程的优先级指针

    线程退出状态

    线程栈地址

    线程栈大小

    修改线程属性的相关函数

    修改线程属性的具体实现


    接下来我们来了解一下什么线程属性

    线程属性中的成员

    线程属性是一个结构体,用法为 pthread_attr_t 变量名(本篇博客里默认变量名为attr)

    线程属性结构体中的成员分别有:线程的警戒缓冲区、线程的优先级指针、线程的退出状态、线程栈地址、线程栈大小

    接下来,我们来介绍一下该结构体中的这几个成员

    线程的警戒缓冲区

    首先我们要知道,每当一个线程被创建出来的时候,都会有一个相应的线程栈出现,而栈存在溢出问题(栈的溢出都是上溢),线程栈是申请在堆空间的

    一旦栈发生溢出,数据就会向上覆盖,影响甚至破坏到库、栈区等空间中的数据。但更可怕的是,由于用户对用户空间中的内容具有读写权限,线程栈上溢所导致的对这些内容的修改,系统是不会报错的,只有当数据溢出到内核层时,我们才能够发现问题,但此时已经晚了,数据已经全被破坏了,所以我们需要来给每个线程栈“加个盖子”,也就是所谓的“警戒缓冲区”。

    警戒缓冲区的大小一般为4K,这块内存是不可读写的,所以当线程栈发生上溢,想要修改这块内存中的内容时,系统就会发现有线程非法操作内存,并杀死该线程,这样就可以保护其他内存中的数据

    线程的优先级指针

    表示线程的优先级,一般情况下不建议修改,因为会影响系统的稳定性,一般只有杀毒软件或系统的防御软件才会修改其优先级

    线程退出状态

    线程的退出状态有两种,分别是回收态(PTHREAD_JOINABLE)和分离态(PTHREAD_DETACH)

    线程栈地址

    由于当修改线程属性时,线程还没有被创建,自然也就没有地址可存,所以默认情况下都是nil,表示空

    线程栈大小

    线程栈大小一般情况下都是8M,但是我们知道,8M如果用二进制表示时非常大的数,将这么大的数放进去仅仅表示线程栈的大小其实没什么意义,所以默认情况下,这里存放的数据就是0,表示8M,申请空间时也是申请8M大小的空间

    修改线程属性的相关函数

    在了解了线程属性的组成之后,我们就要来了解一下修改线程属性的相关函数了

    先介绍下一会会用到的几个变量:

    • pthread_attr_t attr ; //定义一个线程属性结构体
    • int exit_state ; //线程属性中的退出状态
    • void* thread_stack_addr ; //线程属性中的线程栈地址
    • size_t thread_stack_size ; //线程属性中的线程栈大小
    函数功能返回值
    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

    修改线程属性的具体实现

    接下来,我们可以根据这些函数来实际操作一下,完成以下两个小任务

    1. 获取线程属性中默认的退出状态(难度:⭐)
    2. 获取默认状态下线程属性中的线程栈地址与大小。将线程属性中的退出状态设置为分离态,修改线程栈地址,修改线程栈大小为1M,并测试使用这种线程属性的线程,系统一共能够创建多少个?(难度:⭐⭐⭐⭐)

    PS:64位机下,即使你修改了线程栈大小,创建的数目和原来还是一样的,因为你的修改是无效的,系统创建的线程栈大小还是8M,修改线程栈大小只有32位机有效

    一、获取线程属性中默认的退出状态,以下是代码实现

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int main()
    10. {
    11. pthread_attr_t attr;
    12. //1.初始化线程属性
    13. pthread_attr_init(&attr);
    14. //2.检测线程属性中的线程退出状态
    15. int detach_status;
    16. pthread_attr_getdetachstate(&attr , &detach_status);
    17. //3.判断是回收态还是分离态
    18. if(detach_status == PTHREAD_CREATE_JOINABLE)
    19. {
    20. printf("线程属性默认为回收态\n");
    21. }
    22. else
    23. {
    24. printf("线程属性默认为分离态\n");
    25. }
    26. //4.释放线程属性结构体内存
    27. pthread_attr_destroy(&attr);
    28. printf("进程退出!\n");
    29. }

    怎么样,是不是很简单呢?  接下来,我们来完成第二个小任务

    二、获取默认状态下线程属性中的线程栈地址与大小。将线程属性中的退出状态设置为分离态,修改线程栈地址,修改线程栈大小为1M,并测试使用这种线程属性的线程,系统一共能够创建多少个,以下是代码实现

    1. //pthread_addr_change.c
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. void* thread_jobs(void* arg)
    12. {
    13. while(1)
    14. {
    15. sleep(1);
    16. }
    17. pthread_exit(NULL);
    18. }
    19. int main()
    20. {
    21. pthread_attr_t attr;
    22. //1.初始化线程属性
    23. pthread_attr_init(&attr);
    24. //2.检测线程属性中的线程退出状态
    25. int detach_status;
    26. pthread_attr_getdetachstate(&attr , &detach_status);
    27. //3.判断是回收态还是分离态并打印
    28. if(detach_status == PTHREAD_CREATE_JOINABLE)
    29. {
    30. printf("线程属性默认为回收态\n");
    31. }
    32. else
    33. {
    34. printf("线程属性默认为分离态\n");
    35. }
    36. //4.将线程属性中的退出态修改为分离态
    37. pthread_attr_setdetachstate(&attr , PTHREAD_CREATE_DETACHED);
    38. //5.获取线程属性中线程栈的初始地址与大小并打印
    39. void* thread_stack_addr;
    40. size_t thread_stack_size;
    41. pthread_attr_getstack(&attr , &thread_stack_addr , &thread_stack_size);
    42. printf("线程栈地址为 %p , 线程栈大小为 %d \n" , thread_stack_addr , (int)thread_stack_size);
    43. //6.通过malloc函数修改线程栈的初始地址,并将线程栈大小改为1M
    44. pthread_t tid;
    45. thread_stack_size = 0x100000;//0x100000代表1M
    46. int flag = 0;
    47. int errno;
    48. while(1)
    49. {
    50. //如果malloc函数的返回值为NULL,就说明分配失败,内存已经用完
    51. if((thread_stack_addr = (void*)malloc(thread_stack_size)) == NULL)
    52. {
    53. perror("thread_addr malloc failed!\n");
    54. exit(0);//进程退出
    55. }
    56. //修改栈初始地址和大小
    57. pthread_attr_setstack(&attr , thread_stack_addr , thread_stack_size);
    58. //创建线程,并判断是否创建失败
    59. if((errno = pthread_create(&tid , &attr , thread_jobs , NULL)) > 0)
    60. {
    61. perror("thread create failed!\n");
    62. exit(0);//进程直接退出
    63. }
    64. else
    65. {
    66. flag++;
    67. printf("flag = %d\n",flag);
    68. }
    69. }
    70. pthread_attr_destroy(&attr);
    71. return 0;
    72. }

    结果如下图所示:

    以上就是本篇博客的全部内容了,大家有什么地方没有看懂的话,可以在评论区留言给我,咱要力所能及的话就帮大家解答解答

    今天的学习记录到此结束啦,咱们下篇文章见,ByeBye!

  • 相关阅读:
    Total derivative
    一、【海报合成的流程】
    学习JAVA的二十二天(基础)
    Python爬虫入门基础学习(一)
    Unreal Engine 与 Blender - 比较指南
    C语言_字符串与指针的爱恨情仇
    MindSpore——详解推荐模型的原理与实践
    IIS系统结构
    26. 删除有序数组中的重复项
    【数据结构】面试OJ题——时间复杂度
  • 原文地址:https://blog.csdn.net/m0_53133879/article/details/133843138