1. 线程的属性
1.1 概念
如前所述,调用 pthread_create()创建线程,可对新建线程的各种属性进行设置。在 Linux 下,使用pthread_attr_t 数据类型定义线程的所有属性。
调用 pthread_create()创建线程时,参数 attr 设置为 NULL,表示使用属性的默认值创建线程。如果不使用默认值,参数 attr 必须要指向一个 pthread_attr_t 对象,而不能使用 NULL。当定义 pthread_attr_t 对象之后 ,需要 使用 pthread_attr_init()函数对该对象进行初始化操作 ,当对象不再使用时,需要使用pthread_attr_destroy()函数将其销毁,函数原型如下所示:
#include <pthread.h>
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
tips: pthread_attr_t 数据结构中包含的属性比较多,就不一一列举了,可能比较关注属性包括:线程栈的位置和大小、线程调度策略和优先级,以及线程的分离状态属性等。Linux 为 pthread_attr_t 对象的每种属性提供了设置属性的接口以及获取属性的接口。
typedef struct
{
int detachstate; // 线程的分离状态
int schedpolicy; // 线程调度策略
structsched_param schedparam; // 线程的调度参数
int inheritsched; // 线程的继承性
int scope; // 线程的作用域
size_t guardsize; // 线程栈末尾的警戒缓冲区大小
int stackaddr_set; // 线程的栈设置
void* stackaddr; // 线程栈的位置
size_t stacksize; // 线程栈的大小
} pthread_attr_t;
1.2 线程栈属性
每个线程都有自己的栈空间,pthread_attr_t数据结构中定义了栈的起始地址以及栈大小,调用函数pthread_attr_getstack()可以获取这些信息,函数pthread_attr_setstack()对栈起始地址和栈大小进行设置,其函数原型如下所示:
#include <pthread.h>
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
下面注意讲解一下参数的含义:
- pthread_attr_setstack()函数
参数 | 含义 |
---|---|
attr | 参数attr 指向线程属性对象 |
stackaddr | 设置栈起始地址为指定值 |
stacksize | 设置栈大小为指定值 |
- pthread_attr_getstack()函数
参数 | 含义 |
---|---|
attr | 参数attr 指向线程属性对象 |
stackaddr | 调用 pthread_attr_getstack()可获取栈起始地址,并将起始地址信息保存在*stackaddr 中 |
stacksize | 调用 pthread_attr_getstack()可获取栈大小,并将栈大小信息保存在参数 stacksize 所指向的内存中 |
如果想单独获取或设置栈大小、栈起始地址,可以使用下面这些函数:
#include <pthread.h> // 首要的头文件
// 设置栈的大小
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
//设置栈的入口地址
int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);
int pthread_attr_getstackaddr(const pthread_attr_t *attr, void **stackaddr);
讲了这么多,结合1和2我们来一个实例:
创建新的线程,将线程的栈大小设置为 4Kbyte。
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
static void *new_thread_start(void *arg)
{
puts("Hello World!");
return (void *)0;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
size_t stacksize;
pthread_t tid;
int ret;
/* 对 attr 对象进行初始化 */
pthread_attr_init(&attr);
/* 设置栈大小为 4K */
pthread_attr_setstacksize(&attr, 4096);
/* 创建新线程 */
ret = pthread_create(&tid, &attr, new_thread_start, NULL);
if (ret)
{
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(-1);
}
/* 等待新线程终止 */
ret = pthread_join(tid, NULL);
if (ret)
{
fprintf(stderr, "pthread_join error: %s\n", strerror(ret));
exit(-1);
}
/* 销毁 attr 对象 */
pthread_attr_destroy(&attr);
exit(0);
}
1.3 分离状态属性
前面介绍了线程分离的概念,如果对现已创建的某个线程的终止状态不感兴趣,可以使用pthread_detach()函数将其分离,那么该线程在退出时,操作系统会自动回收它所占用的资源。
如果我们在创建线程时就确定要将该线程分离,可以修改pthread_attr_t结构中的detachstate线程属性,让线程一开始运行就处于分离状态。调用函数pthread_attr_setdetachstate()设置detachstate线程属性,调用pthread_attr_getdetachstate()获取detachstate线程属性,其函数原型如下所示:
#include <pthread.h>
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
注意:调用 pthread_attr_setdetachstate()函数将detachstate 线程属性设置为参数 detachstate 所指定的值,参数 detachstate 取值如下:
- PTHREAD_CREATE_DETACHED:新建线程一开始运行便处于分离状态,以分离状态启动线程,无法被其它线程调用 pthread_join()回收,线程结束后由操作系统收回其所占用的资源;
- PTHREAD_CREATE_JOINABLE:这是 detachstate 线程属性的默认值,正常启动线程,可以被其它线程获取终止状态信息。
函数 pthread_attr_getdetachstate()用于获取 detachstate 线程属性,将 detachstate 线程属性保存在参数detachstate 所指定的内存中。
以分离状态启动线程的示例:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
static void *new_thread_start(void *arg)
{
puts("Hello World!");
return (void *)0;
}
int main(int argc, char *argv[])
{
pthread_attr_t attr;
pthread_t tid;
int ret;
/* 对 attr 对象进行初始化 */
pthread_attr_init(&attr);
/* 设置以分离状态启动线程 */
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* 创建新线程 */
ret = pthread_create(&tid, &attr, new_thread_start, NULL);
if (ret)
{
fprintf(stderr, "pthread_create error: %s\n", strerror(ret));
exit(-1);
}
sleep(1);
/* 销毁 attr 对象 */
pthread_attr_destroy(&attr);
exit(0);
}
由于现象基本一样,但是本示例的内部的原理是分离了线程,由系统自动回收资源的。
本文参考正点原子的嵌入式LinuxC应用编程。