fuzidage
专注嵌入式、linux驱动 、arm裸机研究

导航

 

线程属性为一个结构体:

typedef struct
{      
    int        detachstate;       //线程的分离状态
    int        schedpolicy;    //线程调度策略
    structsched_param  schedparam;  //线程的调度參数
    int         inheritsched;     //线程的继承性
    int         scope;         //线程的作用域
    size_t       guardsize;     //线程栈末尾的警戒缓冲区大小
    void*       stackaddr;      //线程栈的位置
    size_t       stacksize;        //线程栈的大小
}pthread_attr_t;

一、线程分离状态:detachstate

该属性决定了线程运行任务后以什么方式来结束自己。

(1) PTHREAD_CREATE_DETACHED    ——    分离线程

置为分离线程的线程。当不须要被不论什么线程等待,线程运行完任务后,自己自己主动结束线程,并释放资源。

(2) PTHREAD_CREATE_JOINABLE(缺省)  ——    可汇合线程

可汇合线程为线程的默认状态,这样的情况下,原有的线程等待创建的线程结束。仅仅有当pthread_join()函数返回时。创建的线程才算终止。才干释放自己占用的系统资源。

使用方法:

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

注意:

pthread_exit只用来退出线程,不会释放线程资源。只有在pthread_create后加上pthread_join函数调用才会释放资源。只要有了pthread_join,即使没有调用pthread_exit,线程退出时也会释放内存。
也就是说pthread_exit只是线程的一个出口,和资源的释放无关,pthread_join直接决定资源是否能够及时释放。举个例子:

void *run(void*p) {
    pthread_exit(0); //可以不要,会自动调用
}

int main () {
    pthread_t tid;
    int rc;
    long count = 0;
    while(1) {
        if((rc=pthread_create(&tid, NULL, run,NULL))!=0) {
            printf("ERROR, rc is %d, %ld threads create fail\n", rc, count);
            perror("Fail:");
            return -1;
        }
        //pthread_join(thread,0);
        if(count++%100==0)
            printf("%ld threads created\n", count);
    }
    return 0;
}

把pthread_join(thread,0)注释拿掉,发现无论创建多少个线程都不会再出错了,说明创建的线程资源退出时得到了释放。

 

int rc, state;
pthread_attr_getdetachstate(&attr, &state);
 if(PTHREAD_CREATE_JOINABLE == state)
        printf("pthread_attr_getdetachstate:         
                PTHREAD_CREATE_JOINABLE\n");
else if(PTHREAD_CREATE_DETACHED == state)
        printf("pthread_attr_getdetachstate: 
                PTHREAD_CREATE_DETACHED\n");
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);    

同理,那么将线程设置成分离线程,发现无论创建多少个线程都不会再出错了,说明创建的线程资源退出时得到了释放

void *run(void*p) {
    pthread_exit(0); //可以不要,会自动调用
}
int main () {
    pthread_t tid;
    int rc;
    long count = 0;
        pthread_attr_t attr;
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    while(1) {
        if((rc=pthread_create(&tid, &attr, run,NULL))!=0) {
            printf("ERROR, rc is %d, %ld threads create fail\n", rc, count);
            perror("Fail:");
            return -1;
        }
        //pthread_join(thread,0);
        if(count++%100==0)
            printf("%ld threads created\n", count);
    }
    return 0;
}

二、线程的调度策略:schedpolicy

内核默认调度算法是循环时间分享策略(SCHED_OTHER或SCHED_NORMAL)。

实时调度策略分为两种SCHED_RR和SCHED_FIFO,linux系统中,这两个调度策略都有99个优先级,其优先级数值从1(低优先级)~ 99(高优先级),每个优先级又有一个队列。

(1) SCHED_FIFO(先进先出策略)

  同优先级的实时进程,后进入的进程要等前一个进程释放了CPU,才能够运行。(没有时间片)

  不同优先级的实时进程,高优先级的实时进程能够抢占低优先级的实时进程。

(2) SCHED_RR(轮转策略)

  同优先级的实时进程中,每个进程又是通过获得时间片来分时运行。

  不同优先级的实时进程,高优先级的实时进程能够抢占低优先级的实时进程。

(3) SCHED_OTHER(循环时间分享策略)

一般线程默认是SCHED_OTHER,这种调度策略没有优先级。所有task排队分享时间片,可以用pthread_attr_getschedpolicy,pthread_attr_setschedpolicy获取和设置调度策略。

pthread_attr_t attr;
int policy;
    pthread_attr_init(&attr);
pthread_attr_getschedpolicy(&attr, &policy);
if (policy == SCHED_FIFO)
        printf("SCHED_FIFO policy\n");
else if(policy == SCHED_RR)
        printf("SCHED_RR policy\n");
else if(policy == SCHED_OTHER)
        printf("SCHED_OTHER policy\n");
pthread_attr_setschedpolicy(&attr, SCHED_RR);

三、调度參数 : sched_param  schedparam

这两个函数具有两个參数。第1个參数是指向属性对象的指针。第2个參数是sched_param结构或指向该结构的指针。结构sched_param在文件/usr/include /bits/sched.h中定义例如以下:

struct sched_param
{
       intsched_priority;
};

结构sched_param的子成员sched_priority控制一个优先权值,大的优先权值相应高的优先权。

系统支持的最大和最小优先权值能够用sched_get_priority_max函数和sched_get_priority_min函数分别得到。

struct sched_param param;
ret = pthread_attr_getschedparam(&attr, &param);
param.sched_priority   =   85;        
pthread_attr_setschedparam( &attr,     &param);

注意:假设不是编写实时程序,不建议改动线程的优先级。由于,调度策略是一件很复杂的事情,假设不对使用会导致程序错误,从而导致死锁等问题。

如:在多线程应用程序中为线程设置不同的优先级别,有可能由于共享资源而导致优先级倒置。

四、线程的继承性: inheritsched

(1) PTHREAD_INHERIT_SCHED(缺省) —— 调度属性自创建者线程继承

(2) PTHREAD_EXPLICIT_SCHED —— 调度属性由调度參数和调度策略决定

    继承性决定调度的參数是从创建的进程中继承还是使用在schedpolicy和schedparam属性中显式设置的调度信息。Pthreads不为inheritsched指定默认值,因此假设你关心线程的调度策略和參数,必须先设置该属性。

pthread_attr_getinheritsched(&attr, &schedinherited);
 if(PTHREAD_INHERIT_SCHED == schedinherited)
            printf("pthread_attr_getinheritsched: 
                    PTHREAD_INHERIT_SCHED\n");
 else if(PTHREAD_EXPLICIT_SCHED == schedinherited)
            printf("pthread_attr_getinheritsched: 
                    PTHREAD_EXPLICIT_SCHED\n");
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

五、线程的作用域:scope

线程的竞争范围。

PTHREAD_SCOPE_SYSTEM ——在系统范围内竞争资源。

PTHREAD_SCOPE_PROCESS(Linux不支持)——在进程范围内竞争资源

pthread_attr_getscope(&attr, &scope);
 if(PTHREAD_SCOPE_PROCESS == scope)
            printf("pthread_attr_getscope: 
                    PTHREAD_SCOPE_PROCESS\n");
 else if(PTHREAD_SCOPE_SYSTEM == scope)
            printf("pthread_attr_getscope: 
                    PTHREAD_SCOPE_SYSTEM\n");

六、线程栈末尾的警戒缓冲区大小:guardsize

该属指定线程末尾的警戒缓冲区大小,在缺省的情况下为一个内存页(4096字节)

    ret = pthread_attr_getguardsize(&attr, &size);
    if(0 != ret) {
        ERROR("pthread_attr_getguardsize", ret);
    } else {
        printf("pthread_attr_getguardsize: %lu\n", size);
    }

七、线程栈的位置:stackaddr

void* stackaddr = malloc(PTHREAD_STACK_MIN);
pthread_attr_getstackaddr(&attr, &stackaddr);

八、线程栈的大小:stacksize

size_t size;
ret = pthread_attr_getstacksize(&attr, &size);
if(0 != ret) {
     ERROR("pthread_attr_getstacksize", ret);
} else {
      printf("pthread_attr_getstacksize: %lu\n", size);
}
pthread_attr_destroy(&attr);

九、线程id

pthread_self()

十、 pthread_once一次性初始化

1.传统静态变量控制

在传统的顺序编程中,一次性初始化经常通过使用静态变量来控制。

void func()
{
    Static flag =1;
    While(flag)
        Do_once_init();
    Flag=0;
}

这里循环调用func时,由于静态变量flag的限制,只会调用一次Do_once_init()

但是在多线程程序设计中,事情就变的复杂的多。如果多个线程并发地执行func,那么每个线程进入。

void do_once_init()
{
        puts("do once init.");
}
void func(void)
{
        static int flag = 1;
        if (flag) {
                do_once_init();
                Flag = 0;
        }
}
static void *thread1(void *data){func();pthread_exit(NULL);}
static void *thread2(void *data){func();pthread_exit(NULL);}
static void *thread3(void *data){func();pthread_exit(NULL);}
int main()
{
        pthread_t tid1,tid2, tid3;
        void*status=NULL;
        pthread_create(&tid1, NULL, thread1, NULL);
        pthread_create(&tid2, NULL, thread2, NULL);
        pthread_create(&tid3, NULL, thread3, NULL);
        pthread_join(tid1,&status);
        pthread_join(tid2,&status);
        pthread_join(tid3,&status);
        return 0;
}

可以看到3个thread同时执行了.

 

如果多个线程并发地执行初始化序列代码,而我们希望该过程本该仅仅执行一次。

2.一次性初始化

如果我们需要对一个posix变量静态的初始化,可使用的方法是用一个互斥量对该变量的初始化进行控制。但有时候我们需要对该变量进行动态初始化,pthread_once就会方便的多。

static pthread_once_t once = PTHREAD_ONCE_INIT;
void do_once_init()
{
        puts("do once init.");
}
void func(void)
{
        do_once_init();
}
static void *thread1(void *data){pthread_once(&once, func);pthread_exit(NULL);}
static void *thread2(void *data){pthread_once(&once, func);pthread_exit(NULL);}
static void *thread3(void *data){pthread_once(&once, func);pthread_exit(NULL);}
int main()
{
        pthread_t tid1,tid2, tid3;
        void*status=NULL;
        pthread_create(&tid1, NULL, thread1, NULL);
        pthread_create(&tid2, NULL, thread2, NULL);
        pthread_create(&tid3, NULL, thread3, NULL);
        pthread_join(tid1,&status);
        pthread_join(tid2,&status);
        pthread_join(tid3,&status);
        return 0;
}

 

posted on 2022-12-26 17:15  fuzidage  阅读(546)  评论(0编辑  收藏  举报