BBS水木清华站∶精华区

发信人: Mccartney (coolcat), 信区: Unix 
标  题: Solaris2.4 多线程编程指南2--用多线程编程 
发信站: BBS 水木清华站 (Sun May 17 16:25:45 1998) 
 
2 用多线程编程 
 
2.1线程(函数)库(The Threads Library) 
 
    用户级多线程是通过线程库,libthread来实现的(参考手册第3页: 
library routines)。线程库支持信号,为可运行的程序排队,并负责同 
时操纵多任务。 
    这一章讨论libthread中的一些通用过程,首先接触基本操作,然后循 
序渐进地进入更复杂的内容。 
创建线程-基本特性       Thr_create(3T)   
获得线程号              Thr_self(3T)     
执行线程                Thr_yield(3T,the below is same)  
挂起或继续线程          Thr_suspend      
                        Thr_continue     
向线程送信号            Thr_kill         
设置线程的调用掩模      Thr_sigsetmask   
终止线程                Thr-exit         
等待线程终止            Thr-join         
维护线程的私有数据      Thr_keycreate    
                        Thr_setspecific  
                        Thr_getspecific  
创建线程-高级特性       Thr_create       
获得最小堆栈容量        Thr_min_stack    
获得或设置线程的同时性等级      Thr_getconcurrency       
                                Thr_setconcurrency       
获得或设置线程的优先级          Thr_getprio      
                                Thr_setprio      
 
2.1.1创建线程-基本篇 
 
    thr_create过程是线程库所有过程当中最复杂的一个。这部分的内容仅 
适用于你使用thr_create的缺省参数来创建进程。 
    对于thr_create更加复杂的使用,包括如何使用自定参数,我们将在高 
级特性部分给出说明。 
    thr_create(3T) 
    这个函数用于在当前进程中添加一个线程。注意,新的线程不继承未处 
理的信号,但继承优先级和信号掩模。 
    #include <thread.h> 
    int thr_create(void *stack_base,size_t stack_size, 
        void *(*start_routine) (void*),void *arg,long flags, 
        thread_t *new_thread); 
    size_t thr_min_stack(void); 
    stack_base--新线程的堆栈地址。如果stack_base是空则thr_create()按 
照stack_size为新线程分配一个堆栈。 
    Stack_size--新线程堆栈的字节数。如果本项为0,将使用缺省值,一般 
情况下最好将此项设为0。 
    并不是每个线程都需要指定堆栈空间。线程库为每个线程的堆栈分配1M 
的虚拟内存,不保留交换空间。(线程库用mmap(2)的MAP_NORESERVE的选项 
来实现这种分配)。 
    Start_routine--指定线程开始执行的函数。如果start_routine返回, 
线程将用该函数的返回值作为退出状态而退出。(参考thr_exit(3T))。 
    Flags--指定新线程的属性,一般设置为0。 
    Flags的值是通过下列内容的位同或来实现的(最后四个flags在高级特性中 
给出)。 
        1. THR_DETACHED 将新线程分离,使得它的线程号和其他资源在线程 
结束时即可以回收利用。当你不想等待线程终止时,将其置位。如果没有明确的 
同步需求阻碍,一个不挂起的,分离的线程可以在创建者的thr_create返回之前 
终止并将其线程号分配给一个心得线程。 
        2. THR_SUSPENDED挂起新线程,直到被thr_continue唤醒。 
        3. THR_BOUND把新线程永久绑定在一个LWP上(生成一个绑定线程)。 
        4. THR_NEW_LWP将非绑定线程的同时性级别加1。 
        5. THR_DAEMON新线程为一个守护线程。 
    New_thread--指向存储新线程ID的地址。多数情况下设置为0。 
    Return Values--thr_create()在成功执行后返回0并退出。任何其他返回值 
表明有错误发生。当以下情况被检测到时,thr_create()失败并返回响应的值。 
    EAGAIN      :超出了系统限制,例如创建了太多的LWP。 
    ENOMEM:可用内存不够创建新线程。 
    EINVAL:stack_base不是NULL而且stack_size比thr_minstack()函数返回的 
最小堆栈要小。 
 
2.1.2获取线程号 
 
    thr_self(3T)    获得自身的线程号。 
        #include <thread.h> 
        thread_t thr_self(void) 
        返回值--调用者的线程号。 
 
2.1.3放弃执行 
 
    thr_yield(3T) 
    thr_yield停止执行当前线程,将执行权限让给有相同或更高优先权的线程。 
        #include <thread.h> 
        void thr_yield(void); 
 
2.1.4挂起或继续执行线程 
 
thr_suspend(3T) 挂起线程。 
        #include <thread.h> 
        int thr_suspend(thread_t target_thread); 
    thr_suspend()立即挂起由target_thread指定的线程。在thr_suspend成功 
返回后,挂起的线程不再执行。后继的thr_suspend无效。 
    Return Values--执行成功后返回0。其他返回值意味着错误。以下情况发生 
时,thr_suspend()失败并返回相关值。 
    ESRCH: 在当前进程中找不到target_thread。 
 
Thr_continue(3T) 
    Thr_continue()恢复执行一个挂起的线程。一旦线程脱离挂起状态,后继的 
thr_continue将无效。 
        #include <thread.h> 
        int thr_continue(thread_t target_thread);  
    一个挂起的线程不会被信号唤醒。信号被挂起知道线程被thr-continue恢复 
执行。 
   返回值--成功执行后返回0。其他值意味着错误。在以下情况发生时,函数失 
败并返回相关值。 
        ESRCH:target_thread在当前进程中找不到。 
 
2.1.5向线程发信号 
 
thr_kill(3T)向线程发信号 
        #include <thread.h> 
        #include <signal.h> 
        int thr_kill(thread_t target_thread,int sig); 
    thr_kill向线程号为target_thread的线程发送信号sig。Target_thread一定 
要与调用线程处于同一个进程内。参数sig一定是signal(5)中定义过的。 
    当sig是0时,错误检查将被执行,没有实际的信号被发送。这可以用来检测 
target_thread参数是否合法。 
    返回值--成功执行后返回0,其他值意味着错误。在以下情况发生时,函数失 
败并返回相关值。 
        EINVAL:sig非法; 
        ESRCH:target_thread找不到; 
 
2.1.6设置本线程的信号掩模 
 
thr_sigsetmask(3T) 获取或改变本线程的信号掩模(signal mask) 
        #include <thread.h> 
        #include <signal.h> 
        int thr_sigsetmask(int how,const sigset_t *set,sigset_t *oset); 
    how参数决定信号设置将被如何改变,可以是下列值之一: 
    SIG_BLOCK--在当前信号掩模上增加set,set指要阻塞的信号组。 
    SIG_UNBLOCK--在当前信号掩模上去掉set,set指要解除阻塞的信号组。 
    SIG_SETMASK--用新的掩模代替现有掩模,set指新的信号掩模。 
    当set的值是NULL时,how的值并不重要,信号掩模将不被改变。所以,要查 
询当前的信号掩模,就给set赋值为NULL。 
    当参数oset不是NULL时,它指向以前的信号掩模存放的地方。 
    Return Values--正常执行后返回0。其他值意味着错误。在以下情况发生时, 
函数失败并返回相关值。 
        EINVAL:set不是NULL且how没有被定义; 
        EFAULT:set或oset不是合法地址; 
 
2.1.7终止线程 
 
thr_exit(3T) 
用来终止一个线程。 
        #include <thread.h> 
        void thr_exit(void *status); 
    thr_exit 函数终止当前线程。所有的私有数据被释放。如果调用线程不是一 
个分离线程,线程的ID和返回状态保留直到有另外的线程在等待。否则返回状态 
被忽略,线程号被立刻重新使用。 
    返回值--当调用线程是进程中的最后一个非守护线程,进程将用状态0退出。 
当最初的线程从main()函数中返回时进程用该线程main函数的返回值退出。 
    线程可以通过两种方式停止执行。第一种是从最初的过程中返回。第二种是 
提供一个退出代码,通过调用thr_exit()结束。下面的事情依赖于在线程创建时 
flags的设置。 
    线程A终止的缺省操作(当flags的相应位设为0时,执行缺省操作)是保持 
状态,直到其它线程(不妨设为B)通过"联合"的方式得知线程A已经死亡。联合 
的结果是B线程得到线程A的退出码,A自动消亡。你可以通过位或来给flags的 
THR_DETACHED参数置位,使得线程在thr_exit()之后或从最初过程返回后立即消 
亡。在这种情况下,它的退出码不会被任何线程获得。 
    有一个重要的特殊情况,在主线程--即最初存在的线程--从主函数返回或调 
用了exit(),整个进程将终止。所以在主线程中要注意不要过早地从主函数main 
返回。 
    如果主线程仅仅调用了thr_exit(),仅仅是它自己死亡,进程不会结束,进 
程内的其他线程将继续运行(当然,如果所有的线程都结束,进程也就结束了)。 
    如果一个线程是非分离的,在它结束后一定要有其它进程与它"联合",否则 
该线程的资源就不会被回收而被新线程使用。所以如果你不希望一个线程被 
"联合",最好按照分离线程来创建。 
    另外一个flag参数是THR_DAEMON。使用这个标志创建的线程是守护线程,在 
其他线程终止之后,这些线程自动终止。这些守护线程在线程库内部特别有用。 
    守护线程可以用库内函数创建--在程序的其他部分是不可见的。当程序中所 
有的其他线程终止,这些线程自动终止。如果它们不是守护线程,在其它线程终 
止后他们不会自动终止,进程不会自动结束。 
 
2.1.8等待线程结束 
 
thr_join(3T) 用thr_join函数来等待线程终止。 
    #include <thread.h> 
    int thr_join(thread_t wait_for,thread_t *departed,void **status); 
    thr_join()函数阻塞自身所在的线程,直到由wait_for指定的线程终止。指 
定的线程一定与本线程在同一个进程内部,而且一定不是分离线程。当wait_for 
参数为0时,thr_join等待任何一个非分离线程结束。换句话说,当不指定线程 
号时,任何非分离线程的退出将导致thr_join()返回。 
    当departed参数不是NULL时,在thr_join正常返回时它指向存放终止线程ID 
的地址。当status参数不是NULL时,在thr_join正常返回时它指向存放终止线程 
退出码的地址。 
    如果线程创建时指定了堆栈,在thr_join返回时堆栈可以被回收。由它返回 
的线程号可以被重新分配。 
    不能有两个线程同时等待同一个线程,如果出现这种情况,其中一个线程正 
常返回,另外一个返回ESRCH错误。 
    返回值--thr_join()在正常执行后返回0,其他值意味着错误。在以下情况 
发生时,函数失败并返回相关值。 
        ESRCH wait_for不合法,等待的线程为分离现成。 
        EDEADLK 等待自身结束。 
    最后步骤 
        thr_join()有三个参数,提供了一定的灵活性。当你需要一个线程等待 
        直到另外一个指定的线程结束,应当把后者的ID提供为第一参数。如果 
        需要等待到任何其他的线程结束,将第一参数置零。 
    如果调用者想知道是那个线程终止,第二参数应当是储存死线程的ID的地址。 
如果不感兴趣,将该参数置零。最后如果需要知道死线程的退出码,应当指出接 
收该错误码的地址。 
    一个线程可以通过以下的代码等待所有的非守护线程结束: 
        while(thr_join(0,0,0)==0) 
    第三个参数的声明(void **)看上去很奇怪。相应的thr_exit()的参数为 
void *。这样做的意图在于你的错误代码为定长的四字节,c语言给定长4字节的 
定义不能是void型,因为这以为着没有参数。所以用void*。因为thr_join()的 
第三参数必须是一个指向thr_exit()返回值的指针,所以类型必须是void **。 
    注意,thr_join()只在目标线程为非分离时有效。如果没有特殊的同步要求 
的话,线程一般都设置成分离的。 
    可以认为,分离线程是通常意义下的线程,而非分离线程知识特殊情况。 
 
2.1.9简单的例程 
 
    在例子2-1里,一个运行在顶部的线程,创建一个辅助线程来执行fetch过程, 
这个辅助过程涉及到复杂的数据库查询,需要较长的时间。主线程在等待结果的 
时候还有其他事情可做。所以它通过执行thr_join()来等待辅助过程结束。 
    操作结果被当作堆栈参数传送,因为主线程等待spun-off线程结束。在一般 
意义上,用malloc()存储数据比通过线程的堆栈来存储要好一些。???? 
Code Example 2-1 A Simple Threads Program 
Void mainline(){ 
        Char int result; 
        Thread_t helper; 
        Int status; 
         
        Thr_create(0,0,fetch,&result,0,&helper); 
/* do something else for a while */ 
        Thr_join(helper,0,&status); 
        /* it's now safe to use result*/ 

void fetch(int * result){ 
        /*fetch value from a database */ 
        *result=value; 
        thr_exit(0); 

 
2.1.10维护线程专有数据 
 
    单线程C程序有两种基本数据--本地数据和全局数据。多线程C程序增加了 
一个特殊类型--线程专有数据(TSD)。非常类似与全局数据,只不过它是线程 
私有的。 
    TSD是以线程为界限的。TSD是定义线程私有数据的唯一方法。每个线程专有 
数据项都由一个进程内唯一的关键字(KEY)来标识。用这个关键字,线程可以 
来存取线程私有的数据。 
    维护TSD的方法通过以下三个函数进行: 
· thr_keycreate()--创建关键字 
· thr_setspecific()--将一个线程绑定在一个关键字上 
· thr_getspecific()--存储指定地址的值 
 
2.1.10.1 thr_keycreate(3T) 
    thr_keycreate()在进程内部分配一个标识TSD的关键字。关键字是进程内 
部唯一的,所有线程在创建时的关键字值是NULL。 
    一旦关键字被建立,每一个线程可以为关键字绑定一个值。这个值对于绑 
定的线程来说是唯一的,被每个线程独立维护。 
        #include <thread.h> 
        int thr_keycreate(thread_key_t keyp, 
                void (*destructor)(void *value); 
    如果thr_keycreate()成功返回,分配的关键字被存储在由keyp指向的区 
域里。调用者一定要保证存储和对关键字的访问被正确地同步。 
    一个可选的析构函数,destructor,可以和每个关键字联系起来。如果一 
个关键字的destructor不空而且线程给该关键字一个非空值,在线程退出时该 
析构函数被调用,使用当前的绑定值。对于所有关键字的析构函数执行的顺序 
是不能指定的。 
    返回值--thr_keycreate()在正常执行后返回0,其他值意味着错误。在以 
下情况发生时,函数失败并返回相关值。 
     EAGAIN 关键字的名字空间用尽 
     ENOMEM 内存不够 
 
2.1.10.2 Thr_setspecific(3T) 
 
        #include <thread.h> 
        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 <thread.h> 
        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 <thread.h> 
        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建立你自己的堆栈 
 
    如果你指定了线程堆栈的大小,要保证你考虑到了调用它的函数和它调用 
的函数需要的空间。需要把调用结果、本地变量和消息结构的成分都考虑进来。 
    偶尔你需要一个与缺省堆栈略有不同的堆栈。一个典型的情况是当线程需 
要一兆以上的堆栈空间。一个不太典型的情况是缺省堆栈对于你来说太大了。 
你可能会创建上千个线程,如果使用缺省堆栈时,就需要上G的空间。 
    堆栈的上限是很显然的,但下限呢?一定要有足够的堆栈空间来保存堆栈 
框架和本地变量。 
    你可以用thr_min_stack()函数来获得绝对的最小堆栈容量,它返回运行一 
个空过程所需要的堆栈空间。有实际用途的线程需要的更多,所以在减小线程 
堆栈的时候要小心。 
    你通过两种方式指定一个堆栈。第一种是给堆栈地址赋空值,由实时的运 
行库来为堆栈分配空间,但需要给stack_size参数提供一个期望的值。 
    另外一种方式是全面了解堆栈管理,为thr_create函数提供一个堆栈的指 
针。这意味着你不但要负责为堆栈分配空间,你还要考虑在线程结束后释放这 
些空间。 
    在你为自己的堆栈分配空间之后,一定要调用一个mprotect(2)函数来为它 
附加一个红区。 
    Start_routine--指定新线程首先要执行的过程。当start_routine返回时, 
线程用该返回值作为退出码退出(参考thr_exit(3T))。 
    注意,你只能指定一个参数。如果你想要多参数,把他们作成一个(例如 
写入一个结构)。这个参数可以是任何一个由void说明的数据,典型的是一个 
4字节的值。任何更大的值都需要用指针来间接传送。 
    Flags--指定创建线程的属性。在多数情况下提供0即可。 
    Flags的值通过位或操作来赋。 
      THR_SUSPENDED--新线程挂起,在thr_continue()后再执行 
        start_routine。用这种办法在运行线程之前对它进行操作(例如改变 
        优先级)。分离线程的终止被忽略。 
      THR_DETACHED--将新线程分离,使线程一旦终止,其资源可以得到立刻 
        回收利用。如果你不需要等待线程结束,设置此标志。 
            如果没有明确的同步要求,一个不挂起的,分离的线程可以在它 
        的创建者调用的thr_create函数返回之前终止并将线程号和其他资源 
        移交给其他线程使用。 
      THR_BOUND--将一个新线程永久绑定在一个LWP上(新线程为绑定线程)。 
      THR_NEW_LWP--给非绑定线程的同时性等级加1。效果类似于用 
        thr_setconcurrency(3T)来增加同时性等级,但是使用 
        thr_setconcurrency()不影响等级设置。典型的,THR_NEW_LWP在LWP池 
        内增加一个LWP来运行非绑定线程。 
            如果你同时指定了THR_BOUND和THR_NEW_LWP,两个LWP被创建,一 
        个被绑定在该线程上,另外一个来运行非绑定线程。 
      THR_DAEMON--标志新线程为守护线程。当所有的非守护线程退出后进程 
        结束。守护线程不影响进程退出状态,在统计退出的线程数时被忽略。 
            一个进程可以通过调用exit(2)或者在所有非守护线程调用 
        thr_exit(3T)函数终止的时候终止。一个应用程序,或它调用的一个库, 
        可以创建一个或多个在决定是否退出的时候被忽略的线程。用 
        THR_DAEMON标志创建的线程在进程退出的范畴不被考虑。 
    New_thread--在thr_create()成功返回后,保存指向存放新线程ID的地址。 
调用者负责提供保存这个参数值指向的空间。 
    如果你对这个值不感兴趣,给它赋值0。 
    返回值--thr_thread在正常执行后返回0,其他值意味着错误。在以下情况 
发生时,函数失败并返回相关值。 
        EAGAIN 超过系统限制,例如创建了太多的LWP。 
        ENOMEM 内存不够创建新线程。 
        EINVAL stack_base非空,但stack_size比thr_minstack()的返回值小。 
 
2.1.11.3 Thr_create(3T)例程 
 
    例2-5显示了怎样用一个与创建者(orig_mask)不同的新的信号掩模来创建 
新线程。 
    在这个例子当中,new_mask被设置为屏蔽SIGINT以外的任何信号。然后创建 
者的信号掩模被改变,以便新线程继承一个不同的掩模,在thr_create()返回后, 
创建者的掩模被恢复为原来的样子。 
    例子假设SIGINT不被创建者屏蔽。如果最初是屏蔽的,用相应的操作去掉屏 
蔽。另外一种办法是用新线程的start routine来设置它自己的信号掩模。 
    Code Example 2-5 thr_create() Creates Thread With New Signal Mask 
        thread_t tid; 
        sigset_t new_mask, orig_mask; 
        int error; 
        (void)sigfillset(&new_mask); 
        (void)sigdelset(&new_mask, SIGINT); 
        (void)thr_sigsetmask(SIGSETMASK, &new_mask, &orig_mask): 
        error = thr_create(NULL, 0, dofunc, NULL, 0, &tid); 
        (void)thr_sigsetmask(SIGSETMASK, NULL, &orig_mask); 
 
2.1.12获得最小堆栈 
 
    thr_min_stack(3T) 用thr_min_stack(3T)来获得线程的堆栈下限 
        #include <thread.h> 
        size_t thr_min_stack(void); 
    thr_min_stack()返回执行一个空线程所需要的堆栈大小(空线程是一个创 
建出来执行一个空过程的线程)。 
    如果一个线程执行的不仅仅是空过程,应当给它分配比thr_min_stack()返 
回值更多的空间。 
    如果线程创建时由用户指定了堆栈,用户应当为该线程保留足够的空间。在 
一个动态连接的环境里,确切知道线程所需要的最小堆栈是非常困难的。 
    大多数情况下,用户不应当自己指定堆栈。用户指定的堆栈仅仅用来支持那 
些希望控制它们的执行环境的应用程序。 
    一般的,用户应当让线程库来处理堆栈的分配。线程库提供的缺省堆栈足够 
运行任何线程。 
 
2.1.13设置线程的同时性等级 
 
2.1.13.1 thr_getconcurrency(3T) 
 
    用thr_getconcurrency()来获得期望的同时性等级的当前值。实际上同时活 
动的线程数可能会比这个数多或少。 
        #include <thread.h> 
        int thr_getconcurrency(void) 
    返回值--thr_getconcurrency()为期望的同时性等级返回当前值。 
 
2.1.13.2 Thr_setconcurrency(3T) 
 
    用thr_setconcurrency()设置期望的同时性等级。 
        #include <thread.h> 
        int thr_setconcurrency(new_level) 
    进程中的非绑定线程可能需要同时活动。为了保留系统资源,线程系统的缺 
省状态保证有足够的活动线程来运行一个进程,防止进程因为缺少同时性而死锁。 
    因为这也许不会创建最有效的同时性等级,thr_setconcurrency()允许应用 
程序用new_level给系统一些提示,来得到需要的同时性等级。 
    实际的同时活动的线程数可能比new_level多或少。 
    注意,如果没有用thr_setconcurrency调整执行资源,有多个 
compute-bound(????)线程的应用程序将不能分配所有的可运行线程。 
    你也可以通过在调用thr_create()时设置THR_NEW_LWP标志来获得期望的同 
时性等级。 
    返回值--thr_setconcurrency()在正常执行后返回0,其他值意味着错误。 
在以下情况发生时,函数失败并返回相关值。 
        EAGAIN 指定的同时性等级超出了系统资源的上限。 
        EINVAL new_level的值为负。 
 
2.1.14得到或设定线程的优先级 
 
    一个非绑定线程在调度时,系统仅仅考虑进程内的其他线程的简单的优先级, 
不做调整,也不涉及内核。线程的系统优先级的形式是唯一的,在创建进程时继 
承而来。 
 
2.1.14.1 Thr_getprio(3T) 
 
    用thr_getprio()来得到线程当前的优先级。 
        #include <thread.h> 
        int thr_getprio(thread_t target_thread,int *pri) 
    每个线程从它的创建者那里继承优先级,thr_getprio把target_thread当前 
的优先级保存到由pri指向的地址内。 
    返回值--thr_getprio()在正常执行后返回0,其他值意味着错误。在以下情 
况发生时,函数失败并返回相关值。 
        ESRCH target_thread在当前进程中不存在。 
 
2.1.14.2 Thr_setprio(3T) 
 
    用thr_setprio()来改变线程的优先级。 
        #include <thread.h> 
        int thr_setprio(thread_t target_thread,int pri) 
     
    thr_setprio改变用target_thread指定的线程的优先级为pri。缺省状态下, 
线程的调度是按照固定的优先级--从0到最大的整数--来进行的,即使不全由优先 
级决定,它也占有非常重要的地位。Target_thread将打断低优先级的线程,而让 
位给高优先级的线程。 
     
    返回值--thr_setprio()在正常执行后返回0,其他值意味着错误。在以下情 
况发生时,函数失败并返回相关值。 
        ESRCH target_thread在当前进程中找不到。 
        EINVAL pri的值对于和target_thread相关的调度等级来说没有意义。 
 
2.1.15线程调度和线程库函数 
 
    下面的libthread函数影响线程调度 
 
2.1.15.1 thr_setprio()和thr_getprio() 
    这两个函数用来改变和检索target_thread的优先级,这个优先级在用户级线 
程库调度线程时被引用,但与操作系统调度LWP的优先级无关。 
    这个优先级影响线程和LWP的结合--如果可运行的线程比LWP多的时候,高优 
先级的线程得到LWP。线程的调度是"专横"的,就是说,如果有一个高优先级的线 
程得不到空闲的LWP,而一个低优先级的线程占有一个LWP,则低优先级的线程被 
迫将LWP让给高优先级的线程。 
 
2.1.15.2 thr_suspend()和thr_continue() 
    这两个函数控制线程是否被允许运行。调用thr_suspend(),可以把线程设置 
为挂起状态。就是说,该线程被搁置,即使有可用的LWP。在其他线程以该线程为 
参数调用thr_continue后,线程退出挂起状态。这两个函数应当小心使用--它们 
的结果也许是危险的。例如,被挂起的线程也许是处在互锁状态的,将它挂起可 
能会导致死锁。 
    一个线程可以在创建时用THR_SUSPENDED标志设置为挂起。 
 
2.1.15.3 thr_yield() 
    Thr_yield函数使线程在相同优先级的线程退出挂起状态后交出LWP。(不会有 
更高优先级的线程可运行而没有运行,因为它会通过强制的方式取得LWP)。这个 
函数具有非常重要的意义,因为在LWP上没有分时的概念(尽管操作系统在执行LWP 
时有分时)。 
    最后,应当注意priocntl(2)也会影响线程调度。更详细的内容请参照"LWP和 
调度等级"。 
 
-- 
※ 来源:·BBS 水木清华站 bbs.net.tsinghua.edu.cn·[FROM: sys11.cic.tsing] 

BBS水木清华站∶精华区