对于线程,你真的懂了吗?

在这里插入图片描述

背景

上周在处理一个问题,涉及到了线程调度策略的问题。因为我们要求线程的属性默认为实时策略(SCHED_RR),但是通过proc文件系统查看,实际生效的是分时调度策略(SCHED_OTHER)。通过分析,发现是由于创建线程是设置的参数并不正确,导致的问题。也顺便复习和了解了一下线程创建属性的知识点。在这里做一个记录,当做是以后的复习知识点之一。
我们知道创建线程的API为

int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);

其中const pthread_attr_t *restrict_attr参数是可以设置线程属性的,但我们平时一般传入NULL,为默认属性,比如调度策略为分时调度,优先级为0等默认参数。但事实证明这个参数的重要性,我们接下来会好好分析一下这个参数的作用:
如果我们想要使用该参数,要遵循以下步骤:

  1. pthread_attr_init:初始化
  2. pthread_attr_destory:去初始化
    pthread_attr_init之后,参数里面的值都是操作系统中默认值了。
    线程的属性结构如下:
typedef struct 
{
    int detachstate;线程的分离状态
    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_t变量进行赋值,必须通过提供的特定接口去设置其中的属性。这也实现了跨平台的功能。下面开始重点讲这里面参数的意义!!!

线程的分离状态

detachstate决定一个线程以什么样的方式来终止自己,默认情况下线程是非分离状态的;
首先,我们知道,创建一个线程之后,(线程默认是非分离的)。我们需要pthread_join等待线程结束,再进行回收,否则会造成内存泄漏或僵尸线程等。但是当pthread_join被调用之后,调用线程就会被阻塞,不能进行下面的操作,这也是非常麻烦的一件事。这个时候分离就能给我提供了这样的机会,创建一个分离的线程,主线程就不需要等待它结束在释放资源,被创建的线程结束后,会自己释放相关资源。使用方式如下:

#include<pthread.h>
int pthread_attr_getdetachstate(const pthread_attr_t * attr,int * detachstate);
int pthread_attr_setdetachstate(pthread_attr_t *sttr,int detachstate);
参数:
          attr:线程属性变量
          detachstate:分离状态属性
          PTHREAD_CREATE_DETACHED 分离状态启动
          PTHREAD_CREATE_JOINABLE   正常启动线程
成功返回0,失败返回-1

线程的继承性

inheritsched决定子线程的调度策略和调度参数使用的是attr参数里面的,还是默认继承父线程的。我们设置实时策略(SCHED_RR)不成功就是因为这个原因。由于默认是继承父线程的属性,而父线程的属性默认为分时调度。导致设置失败。使用方式如下:

#include <pthread.h>
int pthread_attr_getinheritsched(const pthread_attr_t * attr,int * inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t * attr,int inheritsched);
参数: 
        attr :线程参数属性
        inheritsched:线程的继承性
        PTHREAD_INHERIT_SCHED  新的线程继承创建线程的策略和调度参数
        PTHREAD_EXPLICIT_SCHED 新的线程使用attr中的调度策略和调度参数
成功返回0,失败返回-1

调度策略

schedpolicy决定线程将以什么调度策略被调度。linux下的调度策略有三种:
SCHED_OTHER 分时调度
SCHED_FIFO 实时调度(先进先出)
SCHED_RR 实时调度(公平轮转法)
关于三者之间的区别,有兴趣的朋友可以看我相应的文章。使用方式如下:

#include <pthread.h>
int pthread_attr_getschedpolicy(const pthread_attr_t * attr, int * policy);
int pthread_attrsetschedpolicy(pthread_attr_t * attr, int plicy);
参数: 
        attr :线程参数属性
        policy:调度策略     
        SCHED_OTHER 分时调
        SCHED_FIFO  实时调度(先进先出)
        SCHED_RR  实时调度(公平轮转法)
成功返回0,失败返回-1

调度参数

schedparam决定调度策略中的一些参数选择,目前一般仅支持设备优先级。在头文件中的定义如下:

struct sched_param
{
        int sched_priority;参数的本质就是优先级
}
使用方式如下:
#include <pthread.h>
int pthread_attr_getschedparam(const pthread_attr_t * attr, sched_param* param);
int pthread_attr_setschedparam(pthread_attr_t * attr,sched_param * param);
参数: 
         attr :线程参数属性
        param: sched_param结构体
成功返回0,失败返回-1.
注意:系统提供了获取最大优先级和最小优先级的接口
int sched_get_priority_max(int policy);
int sched_get_priority_min(int policy);
参数: 
        policy:调度策略,SCHED_OTHER,SCHED_FIFO,SCHED_RR
         return   对应调度策略最大或最小的优先级
注意:实时的调度策略的优先级范围是1~99.
           分时的调度策略的优先级范围是0

线程的作用域

scope决定线程的作用域,也就是说和哪些线程竞争资源。可以设置为同进程内竞争资源,也可以设置为系统内竞争资源。如果是同进程内竞争资源,当正在运行的线程优先级不高但不是同进程里面的线程,而该线程的优先级较高并且是实时策略。也是无法进行抢占资源的。反之如果是系统内竞争资源,就可以抢占。使用方式如下:

#include<pthread.h>
int pthread_attr_getscope(const pthread_attr_t * attr,int * scope);
int pthread_attr_setscope(pthread_attr_t * attr,int * scope);
参数:
        attr :线程参数属性
        scope:线程的作用域
        PTHREAD_SCOPE_PROCESS  进程内竞争资源
        PTHREAD_SCOPE_SYSTEM   系统级竞争资源

线程堆栈大小

stacksize决定线程栈的大小。因为我们默认线程创建之后,线程栈的大小,默认为8M,这对于内存比较紧张的嵌入式平台而言是巨大的浪费。所以我们需要对线程的业务需求设置合适的栈空间大小。因为当我们的局部变量占用空间超过栈空间,就会引起coredump。栈最小为16kb。使用如下:

#include<pthread.h>
int pthread_attr_getstacksize(const pthread_attr_t * attr,size_t * stacksize);
int pthread_attr_setsstacksize( pthread_attr_t * attr,size_t * stacksize);
参数:
        attr :线程参数属性
        stacksize:堆栈大小
成功返回0,失败返回-1.

线程堆栈地址

stackaddr决定栈空间的地址,具体用在什么方面,我也不清楚,目前知道需要注意的是:设定的栈地址必须以linux页面大小对齐。使用如下:

#include<pthread.h>
int pthread_attr_getstack(const pthread_attr_t * attr,void * stackaddr);
int pthread_attr_setstackaddr(pthread_attr_t * attr,void * stackaddr);
参数:
        attr :线程参数属性
        stackaddr:栈地址
成功返回0,失败返回-1.

警戒缓冲区

guardsize决定线程栈末尾之后以避免栈溢出的扩展内存大小。我们知道当我们局部变量较多占用空间超过栈空间大小时,就会发生coredump。而该值是保证超过多少以内不会抛出异常的保护,相当于扩容。使用如下:

#include<pthread.h>
int pthread_attr_getguardsize(const pthread_attr_t * attr,size_t*guardsize);
int pthread_attr_setguardsize(pthread_attr_t * attr,size_t * guardsize);
参数:
        attr :线程参数属性
       guardsize:警戒缓冲区的大小
成功返回0,失败返回-1.

若我的内容对您有所帮助,还请关注我的公众号。不定期分享干活,剖析案例,也可以一起讨论分享。
我的宗旨:
踩完您工作中的所有坑并分享给您,让你的工作无bug,人生尽是坦途

请添加图片描述

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢艺华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值