1.线程基本属性
包括:栈大小、调度策略和线程状态。通常先创建一个属性对象,然后在属性对象上设置属性的值,再将属性对象传给pthread_create 函数的第二个参数用来创建含有该属性的线程。
2.属性对象
(1)初始化属性对象
pthread_attr_init()函数用于将属性对象使用默认值进行初始化,函数原型如下: int pthread_attr_init(pthread_attr_t *attr);
函数只有一个参数,是一个指向 pthread_attr_t 的属性对象的指针。成功返回 0,否则返回一个非 0 的错误码。
(2)销毁属性对象
销毁属性对象使用 pthread_attr_destroy()函数,函数原型如下: int pthread_attr_destroy(pthread_attr_t *attr);
函数只有一个参数,是一个指向 pthread_attr_t 的属性对象的指针。成功返回 0,否则返回一个非 0 的错误码。
3.线程状态
线程可以有两种状态,分别是:
-
PTHREAD_CREATE_JOINABLE——非分离线程;
-
PTHREAD_CREATE_DETACHED——分离线程。
(1)获取线程状态
获取线程状态的函数是 pthread_attr_getdetachstate(),原型如下:
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
参数 attr 是一个指向已初始化的属性对象的指针,detachstate 是所获取状态值的指针。成功返回 0,否则返回一个非 0 的错误码。
(2)设置线程状态
设置线程状态的函数是 pthread_attr_setdetachstate(),原型如下:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
参数 attr 是一个指向已初始化的属性对象的指针, detachstate 是要设置的值。成功返回0,否则返回一个非 0 的错误码。
4. 线程栈
每个线程都有一个独立的调用栈,线程的栈大小在线程创建的时候就已经固定下来,Linux 系统线程的默认栈大小为 8MB,只有主线程的栈大小会在运行过程中自动增长。用户可以通过属性对象来设置和获取栈大小。
(1)获取线程栈
获取线程栈大小的函数是 pthread_attr_getstacksize(),原型如下:
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
参数 attr 是一个指向已初始化的属性对象的指针, stacksize 是保存所获取栈大小的指针。成功返回 0,否则返回一个非 0 的错误码。
(2)设置线程栈
设置线程栈大小的函数是 pthread_attr_setstacksize(),原型如下:
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
参数 attr 是一个指向已初始化的属性对象的指针,stacksize 是需要设置的栈大小。成功返回 0,否则返回一个非 0 的错误码。
5.线程范例 3
程序清单 13.3 举例说明了线程创建及线程属性的使用方法,主线程根据参数给出的线程栈大小来设置线程属性对象,然后使用剩余参数分别创建线程来实现小写转大写的功能并打印出栈地址
环境:ubuntu16.04 LTS 虚拟机VMware Workstation 编译软件:Eclipse
#include<pthread.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<ctype.h>
#define handle_error_en(en,msg) \
do{errno=en;perror(msg);exit(EXIT_FAILURE);}while(0)
#define handle_error(msg) \
do{perror(msg);exit(EXIT_FAILURE);}while(0)
struct thread_info{
pthread_t thread_id;
int thread_num;
char *argv_string;
};
static void *thread_start(void *arg)
{
struct thread_info *tinfo=arg;
char *uargv,*p;
printf("Thread %d:top of stack near %p;argv_string=%s\n",
tinfo->thread_num,&p,tinfo->argv_string);
uargv = strdup(tinfo->argv_string); //复制字符串,返回字符串副本的地址。
if(uargv == NULL)
{
handle_error("strdup");
}
for(p=uargv;*p!='\0';p++)
*p=toupper(*p);
return uargv;
}
int main(int argc,char *argv[])
{
int s,tnum,opt,num_threads;
struct thread_info *tinfo;
pthread_attr_t attr;
int stack_size;
void *res;
stack_size=-1;
while((opt = getopt(argc,argv,"s:"))!= -1){
switch(opt){
case 's':
stack_size = strtoul(optarg,NULL,0); //将参数1字符串的转换成数值返回。
break; //optarg:“s”选项的参数字符串
default:
fprintf(stderr,"Usage:%s [-s stack-size]arg...\n",argv[0]);
exit(EXIT_FAILURE);
}
}
num_threads=argc-optind; //剩余参数个数,aoptind:argv的当前索引.
s = pthread_attr_init(&attr);
if(s != 0)
handle_error_en(s,"pthread_attr_init");
if(stack_size >0){
s=pthread_attr_setstacksize(&attr,stack_size);
if(s!=0)
handle_error_en(s,"pthread_attr_setstacksize");
}
tinfo=calloc(num_threads,sizeof(struct thread_info)); //申请n个占size个字节的内存空间,分配内存清零,并返回首地址
if(tinfo == NULL)
handle_error("calloc");
for(tnum = 0;tnum<num_threads;tnum++){
tinfo[tnum].thread_num = tnum + 1;
tinfo[tnum].argv_string = argv[optind + tnum];//optind:argv当前索引值,
s=pthread_create(&tinfo[tnum].thread_id,&attr,&thread_start,&tinfo[tnum]);
if(s!=0)
handle_error_en(s,"pthread_create");
}
s=pthread_attr_destroy(&attr);
if(s!=0)
handle_error_en(s,"pthread_attr_destroy");
for(tnum=0;tnum<num_threads;tnum++){
s=pthread_join(tinfo[tnum].thread_id,&res);
if(s !=0)
handle_error_en(s,"pthread_join");
printf("Joined with thread %d;return value was %s\n",
tinfo[tnum].thread_num,(char *)res);
free(res); //strdup创建的副本,使用malloc分配的内存空间.
}
free(tinfo);//callo函数分配的地址空间
exit(EXIT_SUCCESS);
}
Makefile文件。
all:
gcc pthread_attr.c -o pthread_attr -pthread -lm
此程序使用-s 参数来指定每个新创建线程的栈大小,每个线程运行起来后都先取出栈变量的地址,通过打印变量地址来大概估计栈的起始地址。然后每个线程将线程参数给出的字符串转换为大写并返回给主线程,主线程使用pthread_join()等待并获取线程的结果。