Solaris2.4 多线程编程指南2--用多线程编程
在正常执行后返回0,其他值意味着错误。在以 下情况发生时,函数失败并返回相关值。
EAGAIN 关键字的名字空间用尽
ENOMEM 内存不够
2.1.10.2 Thr_setspecific(3T)
#include
int thr_setspecific(thread_key_t key,void *value);
thr_setspecific()为由key指定的TSD关键字绑定一个与本线程相关的值。 返回值--thr_setspecific在正常执行后返回0,其他值意味着错误。在以 下情况发生时,函数失败并返回相关值。
ENOMEM 内存不够
EINVAL 关键字非法
2.1.10.3 Thr_getspecific(3T)
#include
int thr_getspecific(thread_key_t key,void **valuep);
thr_getspecific()将与调用线程相关的关键字的值存入由valuep指定的区
域。
返回值--thr_getspecific()在正常执行后返回0,其他值意味着错误。在 以下情况发生时,函数失败并返回相关值。
EINVAL 关键字非法。
2.1.10.5 全局和私有的线程专有数据
例程2-2是从一个多线程程序中摘录出来的。这段代码可以被任意数量的线 程执行,但一定要参考两个全局变量:errno和mywindow,这两个值是因线程而 异的,就是说是线程私有的。
Code Example 2-2 线程专有数据--全局且私有的
Body(){
……
while(srite(fd,buffer,size)==-1){
if(errno!=EINTR){
fprintf(mywindow,"%s\n",strerror(errno));
exit(1);
}
}
………
}
本线程的系统错误代码errno可以通过线程的系统调用来获得,而不是通过 其他线程。所以一个线程获得的错误码与其他线程是不同的。
变量mywindow指向一个线程私有的输入输出流。所以,一个线程的mywindow 和另外一个线程是不同的,因而最终体现在不同的窗口里。唯一的区别在于线程 库来处理errno,而程序员需要精心设计mywindow。
下面一个例子说明了mywindow的设计方法。处理器把mywindow的指针转换成为对_mywindow过程的调用。
然后调用thr_getspecific(),把全程变量mywindow_key和标识线程窗口的输出参数win传递给它。
Code Example 2-3 将全局参考转化为私有参考
#define mywindow _mywindow()
thread_key_t mywindow_key;
FILE * _mywindow(void){
FILE *win;
Thr_getspecific(mywindow_key,&win);
Return(win);
}
void thread_start(…){
…
make_mywindow();
…
}
变量mywindow标识了一类每个线程都有私有副本的变量;就是说,这些变量 是线程专有数据。每个线程调用make_mywindow()来初始化自己的窗口,并且生 成一个指向它的实例mywindow。 一旦过程被调用,现成可以安全地访问mywindow,在_mywindow函数之后,线 程可以访问它的私有窗口。所以,对mywindow的操作就象是直接操作线程私有 数据一样。
Code Example 2-4 显示了怎样设置
Code Example 2-4 初始化TSD
Void make_mywindow(void){
FILE **win;
Static int once=0;
Static mutex_t lock;
Mutex_lock(&lock);
If (!once){
Once=1;
Thr_keycreate(&mywindow_key,free_key);
}
mutext_unlock(&lock);
win=malloc(sizeof(*win));
create_window(win,…);
thr_setspecific(mywindow_key,win);
}
void freekey(void *win){
free(win);
}
首先,给关键字mywindow_key赋一个唯一的值。这个关键字被用于标识 TSD。所以,第一个调用make_mywindow的线程调用thr_keycreate(),这个函 数给其第一个参数赋一个唯一的值。第二个参数是一个析构函数,用来在线程 终止后将TSD所占的空间回收。
下一步操作是给调用者分配一个TSD的实例空间。分配空间以后,调用 create_window过程,为线程建立一个窗口并用win来标识它。最后调用 thr_setspecific(),把win(即指向窗口的存储区)的值与关键字绑在一起。
做完这一步,任何时候线程调用thr_getspecific(),传送全局关键字, 它得到的都是该线程在调用thr_setspecific时与关键字绑定的值。 如果线程结束,在thr_keycreate()中建立的析构函数将被调用,每个析构 函数只有在终止的线程用thr_setspecific()为关键字赋值之后才会执行。
2.1.11创建线程--高级特性
2.1.11.1 thr_create(3T)
#include
int thr_create(void *stack_base,size_t stack_size,
void *(*start_routine)(void *),void * arg,
long flags,thread_t *newthread);
size_t thr_min_stack(void);
stack_base--新线程所用的堆栈地址。如果本参数为空,thr_create为新线程分配一个至少长stack_size的堆栈。
Stack_size--新线程使用堆栈的字节数。如果本参数为零,将使用缺省值。如果非零,一定要比调用thr_min_stack()获得的值大。
一个最小堆栈也许不能容纳start_routine需要的堆栈大小,所以如果 stack_size被指定,一定要保证它是最小需求与start_routine及它所调用的 函数需要的堆栈空间之和。
典型情况下,由thr_create()分配的线程堆栈从一个页边界开始,到离指 定大小最接近的页边界结束。在堆栈的顶部放置一个没有访问权限的页,这样, 大多数堆栈溢出错误发生在向越界的线程发送SIGSEGV信号的时候。由调用者分 配的线程堆栈 are used as is . ????
如果调用者使用一个预分配的堆栈,在指向该线程的thr_join()函数返回 之前,堆栈将不被释放,即使线程已经终止。然后线程用该函数的返回值作为 退出码退出。
通常情况下,你不需要为线程分配堆栈空间。线程库为每个线程的堆栈分 配一兆的虚拟内存,不保留交换空间(线程库用mmap(2)的MAP_NORESERVE选项 来进行分配)。
每个用线程库创建的线程堆栈有一个"红区"。线程库将一个红区放置在堆 栈顶部来检测溢出。该页是没有访问权限的,在访问时将导致一个页错误。红 区被自动附加在堆栈顶端,不管是用指定的容量还是缺省的容量。
只有在你绝对确信你给的参数正确之后才可以指定堆栈。没有多少情况需 要去指定堆栈或它的大小。即使是专家也很难知道指定的堆栈和容量是否正确。 这是因为遵循ABI的程序不能静态地决定堆栈的大小。它的大小依赖于运行时的 环境。
2.1.11.2建立你自己的堆栈
如果你指定了线程堆栈的大小,要保证你考虑到了调用它的函数和它调用的函数需要的空间。需要把调用结果、本地变量和消息结构的成分都考虑进来。
偶尔你需要一个与缺省堆栈略有不同的堆栈。一个典型的情况是当线程需 要一兆以上的堆栈空间。一个不太典型的情况是缺省堆栈对于你来说太大了。 你可能会创建上千个线程,如果使用缺省堆栈
EAGAIN 关键字的名字空间用尽
ENOMEM 内存不够
2.1.10.2 Thr_setspecific(3T)
#include
int thr_setspecific(thread_key_t key,void *value);
thr_setspecific()为由key指定的TSD关键字绑定一个与本线程相关的值。 返回值--thr_setspecific在正常执行后返回0,其他值意味着错误。在以 下情况发生时,函数失败并返回相关值。
ENOMEM 内存不够
EINVAL 关键字非法
2.1.10.3 Thr_getspecific(3T)
#include
int thr_getspecific(thread_key_t key,void **valuep);
thr_getspecific()将与调用线程相关的关键字的值存入由valuep指定的区
域。
返回值--thr_getspecific()在正常执行后返回0,其他值意味着错误。在 以下情况发生时,函数失败并返回相关值。
EINVAL 关键字非法。
2.1.10.5 全局和私有的线程专有数据
例程2-2是从一个多线程程序中摘录出来的。这段代码可以被任意数量的线 程执行,但一定要参考两个全局变量:errno和mywindow,这两个值是因线程而 异的,就是说是线程私有的。
Code Example 2-2 线程专有数据--全局且私有的
Body(){
……
while(srite(fd,buffer,size)==-1){
if(errno!=EINTR){
fprintf(mywindow,"%s\n",strerror(errno));
exit(1);
}
}
………
}
本线程的系统错误代码errno可以通过线程的系统调用来获得,而不是通过 其他线程。所以一个线程获得的错误码与其他线程是不同的。
变量mywindow指向一个线程私有的输入输出流。所以,一个线程的mywindow 和另外一个线程是不同的,因而最终体现在不同的窗口里。唯一的区别在于线程 库来处理errno,而程序员需要精心设计mywindow。
下面一个例子说明了mywindow的设计方法。处理器把mywindow的指针转换成为对_mywindow过程的调用。
然后调用thr_getspecific(),把全程变量mywindow_key和标识线程窗口的输出参数win传递给它。
Code Example 2-3 将全局参考转化为私有参考
#define mywindow _mywindow()
thread_key_t mywindow_key;
FILE * _mywindow(void){
FILE *win;
Thr_getspecific(mywindow_key,&win);
Return(win);
}
void thread_start(…){
…
make_mywindow();
…
}
变量mywindow标识了一类每个线程都有私有副本的变量;就是说,这些变量 是线程专有数据。每个线程调用make_mywindow()来初始化自己的窗口,并且生 成一个指向它的实例mywindow。 一旦过程被调用,现成可以安全地访问mywindow,在_mywindow函数之后,线 程可以访问它的私有窗口。所以,对mywindow的操作就象是直接操作线程私有 数据一样。
Code Example 2-4 显示了怎样设置
Code Example 2-4 初始化TSD
Void make_mywindow(void){
FILE **win;
Static int once=0;
Static mutex_t lock;
Mutex_lock(&lock);
If (!once){
Once=1;
Thr_keycreate(&mywindow_key,free_key);
}
mutext_unlock(&lock);
win=malloc(sizeof(*win));
create_window(win,…);
thr_setspecific(mywindow_key,win);
}
void freekey(void *win){
free(win);
}
首先,给关键字mywindow_key赋一个唯一的值。这个关键字被用于标识 TSD。所以,第一个调用make_mywindow的线程调用thr_keycreate(),这个函 数给其第一个参数赋一个唯一的值。第二个参数是一个析构函数,用来在线程 终止后将TSD所占的空间回收。
下一步操作是给调用者分配一个TSD的实例空间。分配空间以后,调用 create_window过程,为线程建立一个窗口并用win来标识它。最后调用 thr_setspecific(),把win(即指向窗口的存储区)的值与关键字绑在一起。
做完这一步,任何时候线程调用thr_getspecific(),传送全局关键字, 它得到的都是该线程在调用thr_setspecific时与关键字绑定的值。 如果线程结束,在thr_keycreate()中建立的析构函数将被调用,每个析构 函数只有在终止的线程用thr_setspecific()为关键字赋值之后才会执行。
2.1.11创建线程--高级特性
2.1.11.1 thr_create(3T)
#include
int thr_create(void *stack_base,size_t stack_size,
void *(*start_routine)(void *),void * arg,
long flags,thread_t *newthread);
size_t thr_min_stack(void);
stack_base--新线程所用的堆栈地址。如果本参数为空,thr_create为新线程分配一个至少长stack_size的堆栈。
Stack_size--新线程使用堆栈的字节数。如果本参数为零,将使用缺省值。如果非零,一定要比调用thr_min_stack()获得的值大。
一个最小堆栈也许不能容纳start_routine需要的堆栈大小,所以如果 stack_size被指定,一定要保证它是最小需求与start_routine及它所调用的 函数需要的堆栈空间之和。
典型情况下,由thr_create()分配的线程堆栈从一个页边界开始,到离指 定大小最接近的页边界结束。在堆栈的顶部放置一个没有访问权限的页,这样, 大多数堆栈溢出错误发生在向越界的线程发送SIGSEGV信号的时候。由调用者分 配的线程堆栈 are used as is . ????
如果调用者使用一个预分配的堆栈,在指向该线程的thr_join()函数返回 之前,堆栈将不被释放,即使线程已经终止。然后线程用该函数的返回值作为 退出码退出。
通常情况下,你不需要为线程分配堆栈空间。线程库为每个线程的堆栈分 配一兆的虚拟内存,不保留交换空间(线程库用mmap(2)的MAP_NORESERVE选项 来进行分配)。
每个用线程库创建的线程堆栈有一个"红区"。线程库将一个红区放置在堆 栈顶部来检测溢出。该页是没有访问权限的,在访问时将导致一个页错误。红 区被自动附加在堆栈顶端,不管是用指定的容量还是缺省的容量。
只有在你绝对确信你给的参数正确之后才可以指定堆栈。没有多少情况需 要去指定堆栈或它的大小。即使是专家也很难知道指定的堆栈和容量是否正确。 这是因为遵循ABI的程序不能静态地决定堆栈的大小。它的大小依赖于运行时的 环境。
2.1.11.2建立你自己的堆栈
如果你指定了线程堆栈的大小,要保证你考虑到了调用它的函数和它调用的函数需要的空间。需要把调用结果、本地变量和消息结构的成分都考虑进来。
偶尔你需要一个与缺省堆栈略有不同的堆栈。一个典型的情况是当线程需 要一兆以上的堆栈空间。一个不太典型的情况是缺省堆栈对于你来说太大了。 你可能会创建上千个线程,如果使用缺省堆栈

