线程的基本属性(分离,栈大小,保护区,竞争,调度并发)以及TSD特定数据
2016-08-06 16:47
483 查看
线程的属性:
我们知道,在创建一个线程的时候可以指定一个线程的属性,通过第二个参数来指定:pthread_attr_t *attr.
一般情况下,我们都将这个参数设置为空,表示这个线程使用的是默认属性
那么如果,我们要将线程属性设定为一个特定的值的话,这时候:我们就需要一个线程属性变量(类型:pthread_attr_t),这种类型的变量需要首先初始化后才能用,可以调用这个函数(pthread_attr_init)
要销毁这样的变量的话:pthread_attr_destroy
一旦我们初始化一个属性变量后,那么这个线程属性变量就包含了线程的多种属性的初始值(默认的)
1,获取与设置分离属性
detachstat:我们可以通过改变这个值来达到更改默认默认值的作用
我们继续看看detachstat可以为那些值:PTHREAD_CREATE_DETACHED(表示线程的属性是分离的)
PTHREAD_CREATE_JOINABLE(表示线程的分离属性不是分离的,同样这也是默认值)
如果,一开始我们的属性是:不分离的话,也就是采取了默认值的话,就意味着我们的线程程序必须调用
pthread_join来避免僵线程的产生。。。。。
如果一开始我们的线程属性是分离的话,那么即使我们不来调用pthread_join也不会产生僵线程。。。。。。
同样的,我们也可以不去设置属性,在线程函数里面去调用detach函数来避免僵线程的产生。。。。。
2,获取与设置栈的大小
3,获取和设置栈溢出保护区的大小
4,获取与设置线程竞争范围
5,线程的调度策略
6,继承的调度策略
这意味着新创建的线程和调用它的线程是不是一样的调度策略
如果,我们设置成继承,就是一样的调度策略
7,设置或者获取调度参数
其中:第二个参数是一个结构体:
我们只关心其中的一个参数(param.sched_priority)线程的优先级
8,设置或者获取并发级别
我们获取到上面的值是0,表示内核按照自己默认的方式来映射。。。
简单程序:
运行结果:
线程的特定数据:
1,在单线程程序中,我们经常用到全局变量以实现多个函数共享数据
2,但是在多线程程序中,同样存在全局变量,只不过这个全局变量是为了多个线程所共享的
也就说明:其中一个线程如果改变了这个全局变量的话,那么其它线程调用的时候就得不到想要的结果了
3,有时候,我们需要的是一个线程范围的私有全局变量,即使改变也不会对其它线程产生影响
这个时候,POSIX通过维护一定的数据结构来解决这个问题,这个数据结构为:
TSD(Pthread_Specific Data)线程特定数据
我们来看看这个简单的模型:
两个线程(线程0,线程n)
每个线程都有自己的私有属性(线程号,堆栈,优先级等)
另外每个线程还有自己的特有数据(总共有128项,是通过key-> value的形式来访问的,默认情
况下,每个线程都有这128项,那么我们要使用其中的一项,我们首先要先创建一个key,首先从128
项中去查找,这些指针是不是都是空的,如果是空的话,那么说明我们找到了一个空的位置,我们就可以创建
一个key了)
如上:我们的线程中用的时key[1],也就是说:一个线程创建了一个key,那么其它的线程也得到了一个
key,也就是说:创建的这个key指针相当于全局指针变量,供所有的线程用
但是,这个全局指针变量指向的实际数据是不一样的,每个线程都有一个自己的特有数据,指向了不同的内存
这样的话,也就说明,一个线程改变这个数据并不会影响其它的线程的使用啊,,,,
为了实现特定的数据,我们用到了以下的函数:
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
//第一个参数,如果在main的话,每个线程有的私有全局变量
//第二个参数,要销毁的实际指向的数据
int pthread_key_delete(pthread_key_t key);
//总的线程都执行完后,需要删除一开始创建的那个key
void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void));
//只在第一个新创建的线程中被调用
pthread_once_t once_control = PTHREAD_ONCE_INIT;
程序如下:
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
pthread_key_t key_tsd;
//定义一个全局变量
typedef struct tsd
{
pthread_t tid;
char *str;
}tsd_t;
void destroy_routine(void *value)
{
printf("destory......\n");
free(value);
}
void *thread_routine(void *arg)
{
tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
value->tid = pthread_self();
value->str = (char *)arg;
pthread_setspecific(key_tsd, value);
printf("%s setspecific %p\n", (char *)arg, value);
value = pthread_getspecific(key_tsd);
printf("tid = 0x%x str= %s\n", (int)value->tid, value->str);
sleep(2);
value = pthread_getspecific(key_tsd);
printf("tid = 0x%x str= %s\n", (int)value->tid, value->str);
return NULL;
}
int main(void)
{
pthread_key_create(&key_tsd, destroy_routine);
//下面要创建的两个线程就各自都有了这么一个全局变量了
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, thread_routine, "thread1");
pthread_create(&tid2, NULL, thread_routine, "thread2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
//当两个线程都执行完之后,我们就会删除key_tsd
pthread_key_delete(key_tsd);
//然后就会去执行:destroy_routine这个函数的
return 0;
}
运行结果:
可以分析分析,在延时了2秒之后,每个线程都能保留自己的数据,能保证不会被改变、
这就是线程的特定数据
我们知道,在创建一个线程的时候可以指定一个线程的属性,通过第二个参数来指定:pthread_attr_t *attr.
一般情况下,我们都将这个参数设置为空,表示这个线程使用的是默认属性
那么如果,我们要将线程属性设定为一个特定的值的话,这时候:我们就需要一个线程属性变量(类型: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);
一旦我们初始化一个属性变量后,那么这个线程属性变量就包含了线程的多种属性的初始值(默认的)
1,获取与设置分离属性
#include <pthread.h> int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);//更改分离属性的默认值attr:为刚才所初始化的线程属性变量(对于分离属性而言,还是默认值),
detachstat:我们可以通过改变这个值来达到更改默认默认值的作用
我们继续看看detachstat可以为那些值:PTHREAD_CREATE_DETACHED(表示线程的属性是分离的)
PTHREAD_CREATE_JOINABLE(表示线程的分离属性不是分离的,同样这也是默认值)
如果,一开始我们的属性是:不分离的话,也就是采取了默认值的话,就意味着我们的线程程序必须调用
pthread_join来避免僵线程的产生。。。。。
如果一开始我们的线程属性是分离的话,那么即使我们不来调用pthread_join也不会产生僵线程。。。。。。
同样的,我们也可以不去设置属性,在线程函数里面去调用detach函数来避免僵线程的产生。。。。。
2,获取与设置栈的大小
#include <pthread.h> int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); //第二个参数为0的话,表示使用系统定义的栈大小,如果我们自己设置的话,可能会导致一些移植性的问题 //所以通常情况下,我们不会去设置栈的大小,除非有特殊需求 int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);
3,获取和设置栈溢出保护区的大小
#include <pthread.h> int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); int pthread_attr_getguardsize(pthread_attr_t *attr, size_t *guardsize); //默认的大小是4096k
4,获取与设置线程竞争范围
#include <pthread.h> int pthread_attr_setscope(pthread_attr_t *attr, int scope); int pthread_attr_getscope(pthread_attr_t *attr, int *scope); //默认值是系统范围的竞争
5,线程的调度策略
#include <pthread.h> int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); int pthread_attr_getschedpolicy(pthread_attr_t *attr, int *policy);
6,继承的调度策略
#include <pthread.h> int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched); int pthread_attr_getinheritsched(pthread_attr_t *attr, int *inheritsched);
这意味着新创建的线程和调用它的线程是不是一样的调度策略
如果,我们设置成继承,就是一样的调度策略
7,设置或者获取调度参数
#include <pthread.h> int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); int pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param);
其中:第二个参数是一个结构体:
我们只关心其中的一个参数(param.sched_priority)线程的优先级
8,设置或者获取并发级别
#include <pthread.h> int pthread_setconcurrency(int new_level); //new level线程的并发级别,实际上,并不意味着,可并发的线程数是这些,仅仅只是一个并发级别 //并且仅在N:M线程模型中才有效,设置并发级别,仅仅只是给内核一个提示:提供给定级别数量的核心线程 //来映射用户线程是高效的而已。。。。。仅仅是一个提示,并不能保证内核一定按照这种方式来映射啊 int pthread_getconcurrency(void); //Compile and link with -pthread.
我们获取到上面的值是0,表示内核按照自己默认的方式来映射。。。
简单程序:
#include <unistd.h> #include <sys/types.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #define ERR_EXIT(m)\ do\ {\ perror(m);\ exit(EXIT_FAILURE);\ }while(0) int main(void) { pthread_attr_t attr; pthread_attr_init(&attr); //设置一个线程属性变量 int state; pthread_attr_getdetachstate(&attr, &state); if(state == PTHREAD_CREATE_JOINABLE) printf("detachstate:PTHREAD_CREATE_JOINABLE\n"); else if(state == PTHREAD_CREATE_DETACHED) printf("detachstate:PTHREAD_CREATE_DETACHED\n"); size_t size; pthread_attr_getstacksize(&attr, &size); printf("stacksize:%d\n", (int)size); pthread_attr_getguardsize(&attr, &size); printf("guardsize:%d\n", (int)size); int scope; pthread_attr_getscope(&attr, &scope); if(scope == PTHREAD_SCOPE_PROCESS) printf("scope:PTHREAD_SCOPE_PROCESS\n"); if(scope == PTHREAD_SCOPE_SYSTEM) printf("scope:PTHREAD_SCOPE_SYSTEM\n"); int policy; pthread_attr_getschedpolicy(&attr, &policy); if(policy == SCHED_FIFO) printf("policy:SCHED_FIFO\n"); //如果线程具有相同的优先级,那么按照先进先出的优先级来调度 else if(policy == SCHED_RR) printf("policy:SCHED_RR\n"); //表示如果线程的优先级相同的话,那么也是允许后面的线程进行抢占的 else if(policy == SCHED_OTHER) printf("policy:SCHED_OTHER\n"); //如果是其它的情况的话 int inheritsched; pthread_attr_getinheritsched(&attr, &inheritsched); if(inheritsched == PTHREAD_INHERIT_SCHED) printf("inheritsched:PTHREAD_INHERIT_SCHED\n");//跟调用者一致 else if(inheritsched == PTHREAD_EXPLICIT_SCHED) printf("inheritsched:PTHREAD_EXPLICIT_SCHED");//调度需要自己设置 struct sched_param param; pthread_attr_getschedparam(&attr, ¶m); printf("sched_priority:%d\n", param.sched_priority); pthread_attr_destroy(&attr); int level; level = pthread_getconcurrency(); printf("level:%d\n", level); return 0; }
运行结果:
线程的特定数据:
1,在单线程程序中,我们经常用到全局变量以实现多个函数共享数据
2,但是在多线程程序中,同样存在全局变量,只不过这个全局变量是为了多个线程所共享的
也就说明:其中一个线程如果改变了这个全局变量的话,那么其它线程调用的时候就得不到想要的结果了
3,有时候,我们需要的是一个线程范围的私有全局变量,即使改变也不会对其它线程产生影响
这个时候,POSIX通过维护一定的数据结构来解决这个问题,这个数据结构为:
TSD(Pthread_Specific Data)线程特定数据
我们来看看这个简单的模型:
两个线程(线程0,线程n)
每个线程都有自己的私有属性(线程号,堆栈,优先级等)
另外每个线程还有自己的特有数据(总共有128项,是通过key-> value的形式来访问的,默认情
况下,每个线程都有这128项,那么我们要使用其中的一项,我们首先要先创建一个key,首先从128
项中去查找,这些指针是不是都是空的,如果是空的话,那么说明我们找到了一个空的位置,我们就可以创建
一个key了)
如上:我们的线程中用的时key[1],也就是说:一个线程创建了一个key,那么其它的线程也得到了一个
key,也就是说:创建的这个key指针相当于全局指针变量,供所有的线程用
但是,这个全局指针变量指向的实际数据是不一样的,每个线程都有一个自己的特有数据,指向了不同的内存
这样的话,也就说明,一个线程改变这个数据并不会影响其它的线程的使用啊,,,,
为了实现特定的数据,我们用到了以下的函数:
int pthread_key_create(pthread_key_t *key, void (*destructor)(void *));
//第一个参数,如果在main的话,每个线程有的私有全局变量
//第二个参数,要销毁的实际指向的数据
int pthread_key_delete(pthread_key_t key);
//总的线程都执行完后,需要删除一开始创建的那个key
void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
int pthread_once(pthread_once_t *once_control, void(*init_routine)(void));
//只在第一个新创建的线程中被调用
pthread_once_t once_control = PTHREAD_ONCE_INIT;
程序如下:
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m)\
do\
{\
perror(m);\
exit(EXIT_FAILURE);\
}while(0)
pthread_key_t key_tsd;
//定义一个全局变量
typedef struct tsd
{
pthread_t tid;
char *str;
}tsd_t;
void destroy_routine(void *value)
{
printf("destory......\n");
free(value);
}
void *thread_routine(void *arg)
{
tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
value->tid = pthread_self();
value->str = (char *)arg;
pthread_setspecific(key_tsd, value);
printf("%s setspecific %p\n", (char *)arg, value);
value = pthread_getspecific(key_tsd);
printf("tid = 0x%x str= %s\n", (int)value->tid, value->str);
sleep(2);
value = pthread_getspecific(key_tsd);
printf("tid = 0x%x str= %s\n", (int)value->tid, value->str);
return NULL;
}
int main(void)
{
pthread_key_create(&key_tsd, destroy_routine);
//下面要创建的两个线程就各自都有了这么一个全局变量了
pthread_t tid1;
pthread_t tid2;
pthread_create(&tid1, NULL, thread_routine, "thread1");
pthread_create(&tid2, NULL, thread_routine, "thread2");
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
//当两个线程都执行完之后,我们就会删除key_tsd
pthread_key_delete(key_tsd);
//然后就会去执行:destroy_routine这个函数的
return 0;
}
运行结果:
可以分析分析,在延时了2秒之后,每个线程都能保留自己的数据,能保证不会被改变、
这就是线程的特定数据
相关文章推荐
- JAVA9种基本数据类型的大小,以及他们的封装类
- 线程特定数据(TSD)
- ios-创建线程的基本方法和线程的状态以及属性
- Java基本数据类型的大小,他们的封装类以及自动拆箱和装箱
- Java九种基本数据类型的大小,以及他们的封装类。
- Java九种基本数据类型的大小,以及他们的封装类。
- 九种基本数据类型的大小,以及他们的封装类。
- Java九种基本数据类型的大小,以及他们的封装类以及注意关键点
- Java九种基本数据类型和大小以及其封装类
- java八种基本数据类型的大小,以及他们的封装类
- 九种基本数据类型的大小,以及他们的封装类。
- java基础(一)九种基本数据类型的大小,以及他们的封装类
- java中九种基本数据类型的大小,以及他们的封装类。
- linux网络编程之posix 线程(二):线程的属性和 线程特定数据 Thread-specific Data
- 八大基本数据类型的大小,以及他们的封装类
- 九种基本数据类型的大小,以及他们的封装类
- Java八种基本数据类型的大小,以及他们的封装类。
- Java中 八种基本数据类型的大小,以及他们的封装类
- 12.线程特定数据TSD
- 详解线程、了解进程与线程的区别以及线程分离与结合属性