一直以来创建线程的时候,都将线程属性参数 pthread_attr_t 设置为 NULL. 通常来说这都能满足需求。本文将简要介绍 pthread_attr_t 中包含的属性。
1. pthread_attr_t
pthread_create 函数其中一个参数为此类型,表示将创建指定类型的线程,通常,该类型是一个结构体,包含下面这些属性:
- Detach state: 线程的分离属性
- Scope: 线程竞争域
- Inherit scheduler: 调度继承性
- Scheduling policy: 调度策略
- Scheduling priority: 线程优先级
- Guard size: 警界缓冲区大小,默认是一个 PAGE_SIZE(4096KB)
- Stack address: 栈起始地址(栈最低内存地址)
- Stack size: 栈大小
我只是把这些东西列出来而已,所以各位同学请不要拍砖,我知道大多数属性你是看不懂的。
实际上,这里大多数属性你最好不要去更改,我们能够使用的,一般就是线程的分离属性,对于其它的东西,只有在你理解了线程运行机制以及调度策略之后才能够看的懂。
不过,下面还是简单介绍一下不同属性要干什么事吧。
2. 不同属性的作用
2.1 Detach state
这个属性是线程的分离属性,它表示线程结束的时候,是否回收资源(比如它所使用的栈、线程结构体、线程过程函数的返回值等等)。
分离属性有两种情况:
-
: 这种情况是默认的,表示结束的时候不回收资源,需要调用 pthread_join 函数显式回收,这种情况我们早已经学会了。PTHREAD_CREATE_JOINABLE
-
: 表示线程结束了,直接释放所占用的资源,这种情况下不可再使用 pthread_join 函数去 wait 它了。PTHREAD_CREATE_DETACHED
2.2 Scope
线程竞争域,该属性有两种情况:
-
:表示线程和整个操作系统中的线程进行竞争,比如 CPU (在调度的时候,考虑系统中所有的线程).PTHREAD_SCOPE_SYSTEM
-
: 表示线程只和当前进程内的线程进行竞争,比如线程调度的时候,只考虑当前进程中的线程。PTHREAD_SCOPE_PROCESS
在 Linux 中,只支持
PTHREAD_SCOPE_SYSTEM
,因为 Linux 中实现的线程,实际上就是轻量级进程。
用于设置和获取 scope 值的函数如下:
int pthread_attr_setscope(pthread_attr_t *attr,int scope);
int pthread_attr_getscope(const pthread_attr_t *attr,int *scope);
2.3 Inherit scheduler
调度继承性,该属性有两种情况:
-
: 继承进程的调度策略PTHREAD_INHERIT_SCHED
-
: 不使用继承的调度策略,而是使用自己提供的调度策略。PTHREAD_EXPLICIT_SCHED
用于设置和获取 inherit scheduler 值的函数如下:
int pthread_attr_getinheritsched(const pthread_attr_t *attr,int *inheritsched);
int pthread_attr_setinheritsched(pthread_attr_t *attr,int
2.4 Scheduling policy
调度策略,该属性有以下几个值:
-
SCHED_OTHER
-
SCHED_BATCH
-
SCHED_IDLE
-
SCHED_FIFO
-
SCHED_RR
这些调度策略可分成两大类:
- 普通调度策略(或者叫非实时调度策略),主要包括
、SCHED_OTHER
和SCHED_BATCH
这三种。SCHED_IDLE
- 实时调度策略,主要包括
和SCHED_FIFO
。SCHED_RR
关于这些调度策略的含义,本文不详细讨论。
用于设置和获取 policy 值的函数如下:
int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
int pthread_attr_setschedpolicy(pthread_attr_t *attr, int
2.5 Scheduling priority
调度优先级,通常该值通过下面的函数设定和获取。
pthread_attr_setschedparm(pthread_attr_t *attr,
const struct sched_param *param)
pthread_attr_getschedparm(pthread_attr_t *attr,
struct sched_param *param)
struct sched_param {
...
int sched_priority;
...
对于普通调度策略来说,该值只能是 0. 对于动态优先级来说,该值的范围是 [1,99],但是 POSIX 规定,该范围需要通过 sched_get_priority_min 和 sched_get_priority_max 函数来获取。在具体实现中,最小是 32.
2.6 Guard size
警界缓冲区大小,默认是一个 PAGE_SIZE(4096KB). 用来设置和得到线程栈末尾的警戒缓冲区大小。如果线程栈运行到了警界区,就会收到信号。
可以使用下面两个函数来对其进行设置和获取:
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
2.7 Stack address and Stack size
Stack address 表示栈起始地址(栈最低内存地址),一般来说在我们的 x86 或 x64 处理器上,栈都是往低地址的方向生长,所以该值一般表示栈的末尾(栈顶)。
Stack size 表示栈的大小。
可以使用下面两个函数来对其进行设置和获取:
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
对于栈的大小,还可以使用下面的函数:
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
3. 实验
程序 threadattr 可以用来查看线程属性。该程序可以通过命令行传入一个参数,表示栈大小。另外,带参数的情况下,还会修改线程很多默认属性。
3.1 程序清单
// threadattr.c
#define _GNU_SOURCE /* To get pthread_getattr_np() declaration */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
static void display_pthread_attr(pthread_attr_t *attr, char *prefix)
{
int s, i;
size_t v;
void *stkaddr;
struct sched_param sp;
s = pthread_attr_getdetachstate(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getdetachstate");
printf("%sDetach state = %s\n", prefix,
(i == PTHREAD_CREATE_DETACHED) ? "PTHREAD_CREATE_DETACHED" :
(i == PTHREAD_CREATE_JOINABLE) ? "PTHREAD_CREATE_JOINABLE" :
"???");
s = pthread_attr_getscope(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getscope");
printf("%sScope = %s\n", prefix,
(i == PTHREAD_SCOPE_SYSTEM) ? "PTHREAD_SCOPE_SYSTEM" :
(i == PTHREAD_SCOPE_PROCESS) ? "PTHREAD_SCOPE_PROCESS" :
"???");
s = pthread_attr_getinheritsched(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getinheritsched");
printf("%sInherit scheduler = %s\n", prefix,
(i == PTHREAD_INHERIT_SCHED) ? "PTHREAD_INHERIT_SCHED" :
(i == PTHREAD_EXPLICIT_SCHED) ? "PTHREAD_EXPLICIT_SCHED" :
"???");
s = pthread_attr_getschedpolicy(attr, &i);
if (s != 0)
handle_error_en(s, "pthread_attr_getschedpolicy");
printf("%sScheduling policy = %s\n", prefix,
(i == SCHED_OTHER) ? "SCHED_OTHER" :
(i == SCHED_FIFO) ? "SCHED_FIFO" :
(i == SCHED_RR) ? "SCHED_RR" :
"???");
s = pthread_attr_getschedparam(attr, &sp);
if (s != 0)
handle_error_en(s, "pthread_attr_getschedparam");
printf("%sScheduling priority = %d\n", prefix, sp.sched_priority);
s = pthread_attr_getguardsize(attr, &v);
if (s != 0)
handle_error_en(s, "pthread_attr_getguardsize");
printf("%sGuard size = %d, prefix, v);
s = pthread_attr_getstack(attr, &stkaddr, &v);
if (s != 0)
handle_error_en(s, "pthread_attr_getstack");
printf("%sStack address = %p\n", prefix, stkaddr);
printf("%sStack size = 0x%x, prefix, v);
}
static void * thread_start(void *arg)
{
int s;
pthread_attr_t gattr;
/* pthread_getattr_np() is a non-standard GNU extension that
* retrieves the attributes of the thread specified in its
* first argument */
s = pthread_getattr_np(pthread_self(), &gattr);
if (s != 0)
handle_error_en(s, "pthread_getattr_np");
printf("Thread attributes:\n");
display_pthread_attr(&gattr, "\t");
exit(EXIT_SUCCESS); /* Terminate all threads */
}
int main(int argc, char *argv[])
{
pthread_t thr;
pthread_attr_t attr;
pthread_attr_t *attrp; /* NULL or &attr */
int s;
attrp = NULL;
/* If a command-line argument was supplied, use it to set the
* stack-size attribute and set a few other thread attributes,
* and set attrp pointing to thread attributes object */
if (argc > 1) {
int stack_size;
void *sp;
attrp = &attr;
s = pthread_attr_init(&attr);
if (s != 0)
handle_error_en(s, "pthread_attr_init");
s = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (s != 0)
handle_error_en(s, "pthread_attr_setdetachstate");
s = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (s != 0)
handle_error_en(s, "pthread_attr_setinheritsched");
stack_size = strtoul(argv[1], NULL, 0);
s = posix_memalign(&sp, sysconf(_SC_PAGESIZE), stack_size);
if (s != 0)
handle_error_en(s, "posix_memalign");
printf("posix_memalign() allocated at %p\n", sp);
s = pthread_attr_setstack(&attr, sp, stack_size);
if (s != 0)
handle_error_en(s, "pthread_attr_setstack");
}
s = pthread_create(&thr, attrp, &thread_start, NULL);
if (s != 0)
handle_error_en(s, "pthread_create");
if (attrp != NULL) {
s = pthread_attr_destroy(attrp);
if (s != 0)
handle_error_en(s, "pthread_attr_destroy");
}
pause(); /* Terminates when other thread calls exit() */
3.2 编译和运行
- 编译
$ gcc threadattr.c -o threadattr -lphtread
- 不带参数运行
$ ./threadattr
图1 不带参数运行
- 带参数运行
$ ./threadattr 0x3000000
4. 总结
- 知道线程属性包含哪些内容
- 理解分离属性的作用