• Linux系统编程系列之线程属性


     Linux系统编程系列(16篇管饱,吃货都投降了!)

            1、Linux系统编程系列之进程基础

            2、Linux系统编程系列之进程间通信(IPC)-信号

            3、Linux系统编程系列之进程间通信(IPC)-管道

            4、Linux系统编程系列之进程间通信-IPC对象

            5、Linux系统编程系列之进程间通信-消息队列

            6、Linux系统编程系列之进程间通信-共享内存

            7、Linux系统编程系列之进程间通信-信号量组

            8、Linux系统编程系列之守护进程

            9、Linux系统编程系列之线程

            10、Linux系统编程系列之线程属性 

            11、Linux系统编程系列之互斥锁和读写锁

            12、Linux系统编程系列之线程的信号处理

            13、Linux系统编程系列之POSIX信号量

            14、Linux系统编程系列之条件变量

            15、Linux系统编程系列之死锁

            16、 Linux系统编程系列之线程池

    一、什么是线程属性

            线程属性就是线程的属性,是一个用于控制线程行为和功能的参数集合。它可以影响线程的优先级、调度、同步行为和资源管理等方面。线程有许多属性,如分离属性,继承策略,调度策略等。看图

    二、线程属性的特性

            1、线程优先级

            通过设置线程属性,可以控制线程在调度时的优先级。不同的操作系统和语言环境中,线程优先级的范围和具体实现可能会有所不同。

            2、线程调度

            线程属性可以控制线程的调度行为,包括线程调度算法和调度器设置。

            3、同步行为

            线程属性可以控制线程在同步时的行为。例如,可以设置线程的互斥锁或信号量的行为、线程的等待时限等。

            4、资源管理

            线程属性可以控制线程在使用资源时的行为。例如,可以设置线程对内存、网络或文件等资源的访问权限,以及资源使用超限时的响应行为。

            5、线程安全性

            线程属性可以控制线程的安全性,例如,可以设置线程的栈大小、异常处理行为,以及是否允许多个线程同时访问共享资源等。

            总之,线程属性可以为程序员提供对线程行为的细粒度控制,并帮助提高程序执行效率和稳定性。

    三、线程属性的使用步骤

            由于线程属性众多,因此需要的时候不直接设置,而是先将它们置入一个统一的属性变量中,然后再以此创建线程。属性变量是一种内置数据类型,需要使用专门的函数接口进行初始化和销毁。

            1、定义且初始化属性变量 attr

            2、将所需的属性,加入 attr 中

            3、使用 attr 启动线程

            4、销毁 attr

    四、重点介绍

            1、分离属性

            默认情况下,线程启动后处于可接合状态(即未分离),此时的线程可以在退出时让其他线程接合以便释放资源,但若其他线程未及时调用pthread_join()去结合它,它将成为僵尸线程,浪费系统资源。

            因此,若线程退出时无需汇报其退出值,则一般要设置为分离状态,处于分离状态下的线程在退出之后,会自动释放其占用的系统资源。有以下两种方式可以将线程设置为分离状态:

            (1)、在线程启动前,使用分离属性启动线程

            (2)、在线程启动后,使用 pthread_detach() 强制分离

            2、线程的调度策略

            静态优先级

            在Linux中,所有的任务(进程、线程在内核统称为任务,task)被分成两类静态优先级:

            (1)普通任务(静态优先级是0【动态调度策略】)

            (2)系统任务(静态优先级是1-99)

            静态优先级越大,优先级权限也越大(跟FreeRTOS的一样),普通任务是0,意味着会被任意系统任务抢占。静态优先级之所以被称为静态,是因为这种优先级一旦被设定就不能再改变,是任务本身的属性。

            调度策略有以下几种

            1、SCHED_FIFD 以先进先出的排队方式调度

            2、SCHED_RR 以轮转的方式调度

            3、SCHED_OTHER 非实时调度的普通线程(默认)

           (1)、 SCHED_FIFD

            当线程的调度策略为SCHED_FIFD时,其静态优先级必须设置为1-99,这将意味着一旦这种线程处于就绪态时,他能立即抢占任何静态优先级为0的普通线程。采用 SCHED_FIFD 调度策略的线程还遵循以下规则:

            【1】、当它处于就绪态时,就会被放入其所在优先级队列的队尾位置。

            【2】、当被更高优先级的线程抢占后,它会被放入其所在优先级队列的队头位置,当所有优先级比它高的线程不再运行后,它就恢复运行。

            【3】、当它调用 sched_yield()后,它会被放入其所在优先级队列的队尾的位置。

            总的来讲,一个具有 SCHED_FIFD 调度策略的线程会一直运行直到发送I/O请求,或者被更高优先级线程抢占,或者调用 sched_yield()主动让出CPU。

           (2)、 SCHED_RR

            当线程的调度策略为SCHED_RR时,其情况跟SCHED_FIFD是大致一样的,有一点区别:每一个SCHED_RR策略下的线程都将会被分配一个额度的时间片,当时间耗光时,它会被放入其所在优先级队列的队尾的位置。可以用 sched_rr_get_interval()来获得时间片的具体数值。

            (3)、SCHED_OTHER

            当线程的调度策略为SCHED_OTHER时,其静态优先级(static priority)必须设置为0。该调度策略是Linux系统调度的默认策略,处于0优先级别的这些线程按照所谓的动态优先级被调度,而动态优先级起始于线程的nice值,且每当一个线程已处于就绪态但被调度器调度无视时,其动态优先级会自动增加一个单位,这样能保证这些线程竞争CPU的公平性。

            动态优先级

            线程的动态优先级是非实时的普通线程独有的概念(静态优先级设置为0),它会随着线程的运行,根据线程的表现而发生改变。动态优先级数值越大,优先级越低。

            CPU消耗型线程:比如视频解码算法,这类线程只要一运行就黏住CPU不放,这样的线程的动态优先级会被慢慢地降级,这符合我们的预期,因为这类线程不需要很高的响应速度,只要保证有一定的执行时间片。

            IO消耗型线程:比如编辑器,这类线程绝大部分的时间都在睡眠,调度器发现每次调度它它都选择放弃,将CPU让给其他线程,因此会慢慢地提高它的动态优先级(nice--),这样使得这里线程在同等非实时普通线程中,有越来越高的响应速度,表现出更好的交互性能。

    五、相关函数API接口

            1、初始化线程属性变量

    1. // 初始化线程属性变量
    2. int pthread_attr_init(pthread_attr_t *attr);
    3. // 接口说明
    4. 返回值:成功返回0,失败返回非零错误码
    5. 参数attr:要初始化的线程属性变量

            2、销毁线程属性变量

    1. // 销毁线程属性变量
    2. int pthread_attr_destroy(pthread_attr_t *attr);
    3. // 接口说明
    4. 返回值:成功返回0,失败返回非零错误码
    5. 参数attr:要销毁的线程属性变量

            3、设置线程分离属性

    1. // 设置线程分离属性
    2. int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
    3. // 接口说明
    4. 返回值:成功返回0,失败返回错误码
    5. 参数attr:线程属性变量
    6. 参数detachstate:设置为分离属性还是接合属性,有以下两种:
    7. 1)、PTHREAD_CREATE_DETACHED,分离属性
    8. 2)、PTHREAD_CREATE_JOINABLE,接合属性

            4、设置线程静态优先级

    1. // 设置线程静态优先级
    2. int pthread_attr_setschedparam(pthread_attr_t *attr,
    3. const struct sched_param *param);
    4. // 接口说明
    5. 返回值:成功返回0,失败返回错误码
    6. 参数attr:线程属性变量
    7. 参数param:要设置的静态优先级(099

            5、 获取线程静态优先级

    1. // 获取线程静态优先级
    2. int pthread_attr_getschedparam(pthread_attr_t *attr,
    3. struct sched_param *param);
    4. // 接口说明
    5. 返回值:成功返回0,失败返回错误码
    6. 参数attr:线程属性变量
    7. 参数param:存放获取的静态优先级

            6、设置线程动态优先级

    1. // 设置线程动态优先级
    2. int nice(int inc);
    3. // 接口说明
    4. 返回值:成功返回新的动态优先级,失败返回-1
    5. 参数inc:动态优先级(-2019

    六、案例

            使用线程实现数据发送和接收,设置线程属性为分离属性

    1. // 线程的案例
    2. #include
    3. #include
    4. #include
    5. #include
    6. int flag = 0; // 简单的标志位来控制同步
    7. char data[100];
    8. // 线程1的例程函数,用来接收数据
    9. void *recv_routine(void *arg)
    10. {
    11. printf("I am recv_routine, my tid = %ld\n", pthread_self());
    12. // 设置线程分离
    13. pthread_detach(pthread_self());
    14. while(1)
    15. {
    16. if(flag)
    17. {
    18. printf("pthread1 read data: %s\n", data);
    19. memset(data, 0, sizeof(data));
    20. flag = 0;
    21. }
    22. }
    23. }
    24. // 线程2的例程函数,用来发送数据
    25. void *send_routine(void *arg)
    26. {
    27. printf("I am send_routine, my tid = %ld\n", pthread_self());
    28. while(1)
    29. {
    30. printf("please input data:\n");
    31. fgets(data, 100, stdin);
    32. printf("pthread2 send data\n");
    33. flag = 1;
    34. }
    35. }
    36. int main(int argc, char *argv[])
    37. {
    38. pthread_t tid1, tid2;
    39. // 创建线程1,用来接收数据
    40. errno = pthread_create(&tid1, NULL, recv_routine, NULL);
    41. if(errno == 0)
    42. {
    43. printf("pthread create recv_routine success, tid = %ld\n", tid1);
    44. }
    45. else
    46. {
    47. perror("pthread create recv_routine fail\n");
    48. }
    49. // 1、定义线程属性变量
    50. pthread_attr_t attr2;
    51. // 2、初始化线程属性变量
    52. pthread_attr_init(&attr2);
    53. // 3、设置分离属性
    54. pthread_attr_setdetachstate(&attr2, PTHREAD_CREATE_DETACHED);
    55. // 4、创建线程2,用来发送数据,线程拥有分离属性
    56. errno = pthread_create(&tid2, &attr2, send_routine, NULL);
    57. if(errno == 0)
    58. {
    59. printf("pthread create send_routine success, tid = %ld\n", tid2);
    60. }
    61. else
    62. {
    63. perror("pthread create send_routine fail\n");
    64. }
    65. // 5、销毁属性变量
    66. pthread_attr_destroy(&attr2);
    67. // 一定要加这个,否则主函数直接退出,相当于进程退出,所有线程也退出
    68. // 或者加上while(1)等让主函数不退出
    69. pthread_exit(0);
    70. return 0;
    71. }

    七、总结

            线程属性众多,一般建议把线程设置为分离属性,这样可以在线程退出后,及时释放系统资源,同时也可以根据不同的场景来设置线程的优先级。设置线程的属性需要遵循一定的步骤。

  • 相关阅读:
    冒泡排序法的名字由来,排序步骤是什么,最坏情况下的排序次数如何计算得来的呢?
    SATA系列专题之六:浅析NCQ原生指令序列
    15.2 主机探测与路由追踪
    ios16充电卡在80%充不上去了?可以试试这几种办法
    【web】HTTP(s)协议详解(重点:HTTPS 的加密过程&浏览器中输入网址后,发生了什么?)
    3.2-JZ39 数组中出现次数超过一半的数字
    Django实战项目-学习任务系统-查询列表分页显示
    aliexpress API 接入说明
    java如何实现单例设计模式
    大数据-131 - Flink CEP 案例:检测交易活跃用户、超时未交付
  • 原文地址:https://blog.csdn.net/AABond/article/details/133418899