📄 multithread.txt
字号:
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和
调度等级"。
发信人: Mccartney (coolcat), 信区: Unix
标 题: Solaris2.4 多线程编程指南4--操作系统编程
发信站: BBS 水木清华站 (Sun May 17 16:31:05 1998)
4. 操作系统编程
本章讨论多线程编程如何和操作系统交互,操作系统作出什么改变来支持多线
程。
进程--为多线程而做的改动
警告(alarm), 计数器(interval timer), 配置(profiling)
全局跳转--setjmp(3C) 和longjmp(3C)
资源限制
LWP和调度类型
扩展传统信号
I/O 问题
4.1进程--为多线程而做的改变
4.1.1复制父线程
fork(2)
用fork(2)和fork1(2)函数,你可以选择复制所有的父线程到子线程,或者子
线程只有一个父线程???。
Fork()函数在子进程中复制地址空间和所有的线程(和LWP)。这很有用,例如,
如果子进程永远不调用exec(2)但是用父进程地址空间的拷贝。
为了说明,考虑一个父进程中的线程--不是调用fork()的那个--给一个互斥锁
加了锁。这个互斥锁被拷贝到子进程当中,但给互斥锁解锁的线程没有被拷贝。所
以子进程中的任何试图给互斥锁加锁的线程永久等待。为了避免这种情况,用fork()
复制进程中所有的线程。
注意,如果一个线程调用fork(),阻塞在一个可中断的系统调用的线程将返回
EINTR。
Fork1(2)
Fork1(2) 函数在子线程中复制完全的地址空间,但是仅仅复制调用fork1()的
线程。这在子进程在fork()之后立即调用exec()时有用。在这种情况下,子进程不
需要复制调用fork(2)函数的那个线程以外的线程。
在调用fork1()和exec()之间不要调用任何库函数--库函数也许会使用一个由
多个线程操作的锁。
*Fork(2)和fork1(2)的注意事项
对于fork()和fork1(),在调用之后使用全局声明时要小心。
例如,如果一个线程顺序地读一个文件而另外一个线程成功地调用了fork(),
每一个进程都有了一个读文件的线程。因为文件指针被两个线程共享,父进程得到
一些数据,而子进程得到另外一些。
对于fork()和fork1(),不要创建由父进程和子进程共同使用的锁。这仅发生在
给锁分配的内存是共享的情况下(用mmap(2)的MAP_SHARED声明过)。
Vfork(2)
Vfork(2)类似于fork1(),只有调用线程被拷贝到子进程当中去。
注意,子进程中的线程在调用exec(2)之前不要改变内存。要记住vfork()将父
进程的地址空间交给子进程。父进程在子进程调用exec()或退出后重新获得地址空
间。子进程不改变父进程的状态是非常重要的。
例如在vfork()和exec()之间创建一个新线程是危险的。
4.1.2执行文件和终止进程
exec(2)和exit(2)
exec(2)和exit(2)调用和单线程的进程没有什么区别,只是它们破坏所有线程
的地址空间。两个调用在执行资源(以及活动线程)被破坏前阻塞。
如果exec()重建一个进程,它创建一个LWP。进程从这个初始线程开始执行程序。
象平时一样,如果初始线程返回,它调用exit()来破坏整个进程。
如果所有线程退出,进程用0值退出。
4.2 Alarms(闹钟???), Interval Timers(定时器), and Profiling(配置)
每个LWP有一个唯一的实时的定时器和一个绑定在LWP上的线程的闹钟。定时器
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -