线程属性的相关设置详解 - 详解

  1. pthread_attr_t 结构体

线程属性结构如下:

                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;

       detachstate,线程的分离状态决定一个线程以什么样的方式来终止自己。在默认情况下线程是非分离状态的,这种情况下,原有的线程等待创建的线程结束。只有当pthread_join() 函数返回时,创建的线程才算终止,才能释放自己占用的系统资源。分离线程没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。
       通俗的说也就是:我们知道一般我们要等待(pthread_join)一个线程的结束,主要是想知道它的结束状态,否则等待一般是没有什么意义的!但是如果对一些线程的终止态我们压根就不想知道,那么就可以使用“分离”属性,那么我 们就无须等待管理,只要线程自己结束了,自己释放资源就可以。


detachstate有两种取值
  PTHREAD_CREATE_DETACHED:     分离状态启动
  PTHREAD_CREATE_JOINABLE:      聚合状态启动线程

schedpolicy,表示新线程的调度策略,主要包括:
SCHED_OTHER(正常、非实时)
SCHED_RR(实时、轮转法)
SCHED_FIFO(实时、先入先出)
缺省为SCHED_OTHER,

 schedparam,一个struct sched_param结构,目前仅有一个sched_priority整型变量表示线程的运行优先级。这个参数仅当调度策略为实时(即SCHED_RR或SCHED_FIFO)时才有效,

/usr/include /bits/sched.h
     struct sched_param
     {
            int sched_priority;    //!> 参数的本质就是优先级
     };

注意:大的权值对应高的优先级!
                    系统支持的最大和最小的优先级值可以用函数:
                    sched_get_priority_max和sched_get_priority_min得到!

inheritsched,继承性决定调度的参数是从创建的进程中继承还是使用在 
            schedpolicy和schedparam属性中显式设置的调度信息,

可设置参数:
            PTHREAD_INHERIT_SCHED: 新的线程继承创建线程的策略和参数!
            PTHREAD_EXPLICIT_SCHED:新的线程继承策略和参数来自于
 schedpolicy和schedparam属性中显式
设置的调度信息!

scope,表示线程间竞争资源的范围

POSIX的标准中定义了两个值:PTHREAD_SCOPE_SYSTEM和PTHREAD_SCOPE_PROCESS,

目前LinuxThreads仅实现了PTHREAD_SCOPE_SYSTEM一值。

scope有两种取值

PTHREAD_SCOPE_SYSTEM:    与系统中所有线程一起竞争资源      PTHREAD_SCOPE_PROCESS:    仅与同进程中的线程竞争CPU

示例:

 3. 线程属性及属性设置
        3.1 线程属性结构体
                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;

    3.2. 线程属性设置流程
          1)  初始化线程属性对象
                 pthread_attr_init
                 头文件:      #include <pthread.h>  
             函数原型:    int pthread_attr_init(pthread_attr_t *attr);
             函数功能:    初始化线程属性对象
             函数参数:    attr:待初始化的线程属性对象
             函数返回值:  成功返回0
                      失败返回错误码                
          2)  设置指定的属性
                  int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
                      int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

          3)  利用属性创建线程
          4)  回收线程属性对象
                 pthread_attr_destroy
                 头文件:      #include <pthread.h>  
             函数原型:    int pthread_attr_destroy(pthread_attr_t *attr);
             函数功能:    回收线程属性对象
             函数参数:    attr:待回收的线程属性对象
             函数返回值:  成功返回0
                      失败返回错误码

#include       // 提供Unix系统调用
#include      // 多线程编程
#include        // 标准输入输出
#include       // 字符串处理
#include       // 标准库函数
int thread_num = 0;      // 全局变量,用于线程计数
// 线程执行函数
void* routine(void* argp)
{
    thread_num--;        // 线程结束时减少计数
    return NULL;         // 线程正常退出
}
int main(int argc, char** argv)
{
     // 声明线程属性变量
     pthread_attr_t attr;
     // 初始化线程属性变量
     pthread_attr_init(&attr);
     // 获取和设置线程栈大小
     size_t stacksize = 0;
     // 设置线程栈大小为4MB
     pthread_attr_setstacksize(&attr, 4 * 1024 * 1024);
     // 获取实际设置的栈大小
     pthread_attr_getstacksize(&attr, &stacksize);
     printf("缺省线程栈空间大小:%lu MB\n", stacksize / 1024 / 1024);
     // 设置线程为分离状态(detached)
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     // 创建线程,使用自定义属性
     pthread_t id;
     pthread_create(&id, &attr, routine, NULL);
     thread_num++;       // 增加线程计数
     // 销毁线程属性变量(释放资源)
     pthread_attr_destroy(&attr);
     /*
     // 被注释的代码:尝试等待分离线程(会失败)
     int error = 0;
     if(error = pthread_join(id, NULL))
     {
        printf("join:%s\n", strerror(error));
     }
     */
     // 忙等待,直到线程计数为0
     while(thread_num)   ;
     return 0;
}

关键机制详解

1. 线程属性设置

pthread_attr_t attr;
pthread_attr_init(&attr);  // 初始化属性对象

2. 栈大小设置

pthread_attr_setstacksize(&attr, 4 * 1024 * 1024);  // 设置为4MB
pthread_attr_getstacksize(&attr, &stacksize);       // 验证设置

栈大小说明:

  • 默认栈大小通常为2-10MB,取决于系统和编译器

  • 设置更大的栈用于需要大量局部变量的线程

  • 设置更小的栈用于创建大量线程的场景

3. 线程分离状态

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

分离状态 vs 可连接状态:

状态特点是否需要pthread_join资源回收
PTHREAD_CREATE_JOINABLE (默认)可等待线程结束需要手动调用pthread_join回收
PTHREAD_CREATE_DETACHED分离状态不能等待自动回收资源

4. 被注释的pthread_join代码

/*
if(error = pthread_join(id, NULL))
{
   printf("join:%s\n", strerror(error));
}
*/

这里被注释是有原因的:

  • 线程被设置为分离状态后,不能再调用 pthread_join()

  • 如果取消注释,会返回 EINVAL 错误

5. 线程同步机制

while(thread_num) ;  // 忙等待,直到thread_num为0

工作原理:

  • 主线程创建子线程时:thread_num++ (变为1)

  • 子线程结束时:thread_num-- (变为0)

  • 主线程在忙等待循环中检测到这个变化后退出

程序执行流程

  1. 主线程初始化属性,设置栈大小和分离状态

  2. 主线程创建分离线程,增加线程计数

  3. 主线程进入忙等待循环

  4. 子线程执行,减少线程计数

  5. 子线程退出,系统自动回收资源(因为是分离线程)

  6. 主线程检测到thread_num为0,退出循环

  7. 程序结束

潜在问题和改进

1. 忙等待(Busy Waiting)问题

while(thread_num) ;  // CPU占用率高

改进方案:使用条件变量

#include 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
void* routine(void* argp) {
    pthread_mutex_lock(&mutex);
    thread_num--;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    return NULL;
}
// 主线程中:
pthread_mutex_lock(&mutex);
while(thread_num) {
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

2. 内存排序问题

thread_num--;  // 可能存在可见性问题

改进方案:使用原子操作或互斥锁

#include 
atomic_int thread_num = 0;
// 或者使用互斥锁保护

3. 完整的改进版本

#include 
#include 
#include 
#include 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int thread_num = 0;
void* routine(void* argp) {
    pthread_mutex_lock(&mutex);
    thread_num--;
    printf("子线程结束,剩余线程数: %d\n", thread_num);
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);
    return NULL;
}
int main() {
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    // 设置线程属性
    pthread_attr_setstacksize(&attr, 4 * 1024 * 1024);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    // 创建线程
    pthread_t id;
    pthread_mutex_lock(&mutex);
    pthread_create(&id, &attr, routine, NULL);
    thread_num++;
    printf("创建线程,当前线程数: %d\n", thread_num);
    pthread_mutex_unlock(&mutex);
    // 等待线程结束
    pthread_mutex_lock(&mutex);
    while(thread_num > 0) {
        pthread_cond_wait(&cond, &mutex);
    }
    pthread_mutex_unlock(&mutex);
    pthread_attr_destroy(&attr);
    printf("所有线程结束,程序退出\n");
    return 0;
}

这个程序很好地演示了线程属性的使用,特别是栈大小设置和分离状态的配置。

posted @ 2025-10-27 08:34  gccbuaa  阅读(4)  评论(0)    收藏  举报