Linux网络编程之POSIX线程(二)-线程属性与线程特定数据

上一篇文章总结了POSIX线程模型,POSIX相关函数以及使用线程实现的回射客户/服务器。接下来学习线程的属性以及线程特定数据的相关内容。

1.线程属性

(1)pthread_create函数创建线程的时候,可以指定一个线程的属性,我们前面使用该函数的时候由于不关心该属性即为默认值,所以将其置为了NULL,函数原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);

(2)若将线程属性指定为特定的值,就需要指定一个线程属性变量pthread_attr_t,这种变量需要初始化后才能使用,有如下初始化与销毁属性方法:

  • int pthread_attr_init(pthread_attr_t *attr);
  • int pthread_attr_destroy(pthread_attr_t *attr);

一旦初始化了一个属性变量之后,该线程属性变量就包含了线程的多种属性的初始值。使用下面的函数可以做出相应的修改:

(3)获取与设置分离属性

  • int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
  • int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);

1)第一个参数attr是初始化的线程属性变量。
2)通过第二个参数detachstate可以更改,有两个可选值:PTHREAD_CREATE_DETACHED(表示线程属性是分离的),PTHREAD_CREATE_JOINABLE(表示线程属性不是分离的,默认值)

3)若将线程属性设置为PTHREAD_CREATE_DETACHED,那么即使线程退出的时候,而调用者线程没有调用pthread_join,这个线程也不会处于僵线程状态。(而之前我们是通过调用pthread_detach函数实现分离的)

(4)获取与设置栈大小

  • int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
  • int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);

通常,第二个参数stacksize,可以指定为0,表示由系统定义栈大小。

(5)获取与设置栈溢出保护区大小

  • int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
  • int pthread_attr_getguardsize(ptherad_attr_t *attr, size_t *guardsize);

(6)获取与设置线程竞争范围

  • int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope);
  • int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope);

(7)获取与设置调度策略

  • int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy);
  • int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy);

(8)获取与设置继承的调度策略

  • int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched);
  • int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched);

(9)获取与设置调度参数

  • int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param);
  • int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param);

使用上述函数,查看线程的各个属性:

2.并发级别

(1)获取与设置并发级别

  • int pthread_setconcurrency(int new_level);
  • int pthread_getconcurrency(void);

(2)仅在N:M线程模型中有效,设置并发级别,给内核一个提示:表示提供给定级别数量的核心线程来映射用户线程是高效的。

3.线程特定数据

在单线程程序中,我们经常要用到“全局变量”以实现多个函数间共享数据。在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。

(1)POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据成为Thread-specific Data, 或TSD

线程特定数据模型如下:线程特定数据

1)“其他线程信息”包括线程控制块、CPU状态等信息;
2)另外除了“其他线程信息”之外,每个线程还有自己的特定数据——总共有128项(图中pkey[0]-pkey[127]所示)

3)如果需要使用这128项中的一个,首先需要去查找该key的指针是否为空的,如果是空的就可以使用,比如在该指针中存放数据a;但是,多个线程中,虽然每个线程的该指针都被使用了(如图中pkey[1],一旦一个线程中的key被创建,则所有线程都创建了该key,类似于创建的key是个全局变量),但是各个线程在该指针中存放的“实际数据”却不相同

4)这样一来,线程0对该“实际数据”进程修改,并不会影响线程n中对应的“实际数据”。

(2)相关函数

  • 创建一个key
    int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
    第二个参数提供一个函数指针,来销毁“实际数据”。

示例,创建一个key:
pthread_key_t key_tsd;
pthread_key_create(&key_tsd, destroy_routinue);
void destroy_routine(void *value)
{
    ….
}

  • 删除key
    int pthread_key_delete(pthread_key_t key);

示例,删除上述创建的key:
pthread_key_delete(key_tsd);

  • 获取线程特定数据
    void *pthread_getspecific(pthread_key_t key);

 

  • 设置线程特定数据
    int pthread_setspecific(pthread_key_t key, const void *value);

示例,给线程设定特定数据:

typedef struct std {    //自定义要设定的“特定数据”
    pthread_t tid;
    char *str;
}tsd_t;
tsd_t *value = (tsd_t*)malloc(sizeof(std_t));
value->tid = pthread_self();
value->str = (char*)arg;
pthread_setspecific(key_tsd, value);

使用上述函数实现一个程序:

输出结果:

 

最后补充一个函数:
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
pthread_once_t one_control = PTHREAD_ONCE_INIT;

该函数表示,第二个参数,函数指针指向的函数的执行,只在第一个线程进入的时候被执行一次!
例如将创建key的函数pthread_key_create放入线程入口函数thread_routine中,这样一来,每次创建一个线程都会调用一次thread_routine,进而会指向该函数中的pthread_key_create两次。而我们又只想它只创建一次key,这个时候就可以使用pthread_once函数来解决这个问题:

pthread_once_t once_control = PTHREAD_ONCE_INIT;//先定义一个once_control

//再在创建线程的入口函数thread_routine中调用该函数
pthread_once(&once_control, once_routine);
//该函数也要提供一个处理函数,将“动作”放入该函数中,本例是将创建key的函数放入
void once_routine(void)
{
    pthread_key_create(&key_tsd, destroy_routine);
}

这样,当第一个线程调用过一次pthread_once之后,第二个线程进来后发现该函数已被调用过一次,就不会再次调用了。因此pthread_key_create函数只执行了一次,解决问题。

发表评论

电子邮件地址不会被公开。 必填项已用*标注