Solaris2.4 多线程编程指南3--使用同步对象编程
回相关值。
EINVAL 非法参数。
EFAULT rwlp指向一个非法地址。
EBUSY 由rwlp指向的读写锁已被加锁。
3.3.6使一个读写锁退出阻塞状态
rw_unlock(3T)
#include(or #include )
int rwlock_tryrdlock(rwlock_t *rwlp);
用rw_unlock()来使由rwlp指向的读写锁退出阻塞状态。调用线程必须已经获得对该读写锁的读锁或写锁。如果任何其它线程在等待读写锁可用,它们当中的一个将退出阻塞状态。
返回值--rw_unlock ()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 非法参数。
EFAULT rwlp指向一个非法地址。
3.3.7清除读写锁
rwlock_destroy(3T)
#include(or #include )
int rwlock_destroy(rwlock_t *rwlp);
使用rwlock_destroy()来取消由rwlp指向的读写锁的状态。存储读写锁的空间不被释放。
返回值--rw_destroy ()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 非法参数。
EFAULT rwlp指向一个非法地址。
示例3-14用一个银行帐户来演示读写锁。如果一个程序允许多个线程同时进行读操作,一个时刻只有一个写操作被允许。注意get_balance()函数通过锁来保证检查和储存操作是原子操作。
Code Example 3-14 读/写银行帐户
Rwlock_t account_lock;
Float checking_balance=100.0;
Float saving_balance=100.0;
… …
rwlock_init (&account_lock, 0, NULL);
… …
float get_balance(){
float bal;
rw_rdlock(&account_lock);
bal=checking_balance +saving_balance;
rw_unlock(&account_lock);
return(bal);
}
void tranfer_checking_to_savings(float amount) {
rw_wrlock(&account_lock);
checking_balance=checking_balance - amount;
savings_balance=savings_balance +amount;
rw_unlock(&account_lock);
}
3.4信号量(信号灯)
信号灯是E.W.Dijkstra在60年代晚期定义的程序结构。Dijkstra的模型是一个铁路上的操作:一段单线铁路在一个时刻只允许一列火车通过。
用一个信号灯来维护这段铁路。一列火车在进入单线铁路之前必须等待信号灯 的许可。如果一列火车进入这段轨道,信号灯改变状态,以防止其他火车进入。在 火车离开这段轨道时,必须将信号灯复原,使得其他火车得以进入。
在信号灯的计算机版本中,一个信号灯一般是一个整数,称之为信号量。一个 线程在被允许进行后对信号量做一个p操作。
P操作的字面意思是线程必须等到信号量的值为正(positive)才能继续进行, 进行前先给信号量减1。当做完相关的操作时(相当于离开铁轨),线程执行一个 v操作,即给信号量加1。这两个操作必须具有不可中断性,也叫不可分性,英文字 面为原子性(atomic),即他们不能被分成两个子操作,在子操作之间还可以插入 其它线程的其他操作,这些操作可能改变信号量。在P操作中,信号量的值在被减之 前一定要为正(使得信号量在被减1之后不会为负)。
在P操作或V操作当中,操作不会互相干扰。如果两个V操作要同时执行,则信号量的新值比原来大2。
记住P和V本身是什么意思已经不重要了,就象记住Dijkstra是荷兰人一样。但 是,如果引起了学者考证的兴趣,P代表prolagen,一个由proberen de verlagen演 变来的合成词,它的意思是"试图减"。V代表verhogen,它的意思是"增加"。这些在 Dijkstra的技术笔记EWD 74中提到过。
Sema_wait(3T)和sema_post(3T)分别对应Dijkstra的P和V操作, sema_trywait(3T)是P操作的一个可选的形式,在P操作不能执行时,线程不会阻塞, 而是立刻返回一个非零值。
有两种基本的信号量:二值信号量,其值只能是0或者1,和计数信号量,可以 是非负值。一个二值信号量在逻辑上相当于一个互斥锁。
然而,尽管并不强制,互斥锁应当被认为只能被拥有锁的线程释放,而"拥有信 号量的线程"这个概念是不存在的,任何线程都可以进行一个V操作 (或sema_post(3T))。
计数信号量的功能大概和与互斥锁合用的条件变量一样强大。在很多情况下, 采用信号量的程序比采用条件变量要简单一些(如下面的例子所示)。
然而,如果一个互斥锁和条件变量一起使用,有一个隐含的框架,程序的哪一 部分被保护是明显的。在信号量则不然,它可以用同时性编程当中的go to 来调用, 它更适合用于那些结构性不强的,不精确的方面。
3.4.1计数信号量
在概念上,一个信号量是一个非负整数。信号量在典型情况下用来协调资源, 信号量一般被初始化为可用资源的数量。线程在假如资源是给计数器加1,在拿走资 源时给计数器减1,操作都具有原子性。
如果一个信号量的值变为0,表明已无可用资源,想要给信号量减1的操作必须 等到它为正时。
表3-4 信号量函数
函数 操作
Sema_init(3T) 初始化信号量
Sema_post(3T) 增加信号量
Sema_wait(3T) 关于信号量阻塞
Sema_trywait(3T) 减少信号量
Sema_destroy(3T) 破坏信号量的状态
因为信号量不被哪个线程占有,它们可以用异步事件来通知(例如信号处理器)。 而且,因为信号量包含状态,他们可以被异步使用???,而不用象条件变量那样 一定要先获得互斥锁。
缺省情况下,等待信号量的多个线程退出阻塞的顺序是不确定的。
信号量在使用前一定要初始化。
3.4.2初始化一个信号量
sema_init(3T)
#include(or #include )
int sema_init(sema_t *sp, unsigned int count, int type, void *arg);
sema_init用count的值来初始化由sp指向的信号量。Type可以是如下值之一(arg先不谈)。
USYNC_PROCESS 信号量可以在进程间进行线程同步。只有一个进程需要初始化
信号量。Arg忽略。
USYNC_THREAD 信号量只能在进程内部进行线程同步。
多个线程不能同时初始化同一个信号量。一个信号量在使用中不能被其他线程重新初始化。
返回值--sema_init()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 非法参数。
EFAULT sp或arg指向一个非法地址。
3.4.3给信号量增值
sema_post(3T)
#include(or #include )
int sema_destroy(sema_t *sp);
用sema_post()给由sp指向的信号量原子地(表示其不可分性,下同)增1,如果有其它线程关于信号量阻塞,其中一个退出阻塞状态。
返回值--sema_post()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数
EINVAL 非法参数。
EFAULT rwlp指向一个非法地址。
EBUSY 由rwlp指向的读写锁已被加锁。
3.3.6使一个读写锁退出阻塞状态
rw_unlock(3T)
#include
int rwlock_tryrdlock(rwlock_t *rwlp);
用rw_unlock()来使由rwlp指向的读写锁退出阻塞状态。调用线程必须已经获得对该读写锁的读锁或写锁。如果任何其它线程在等待读写锁可用,它们当中的一个将退出阻塞状态。
返回值--rw_unlock ()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 非法参数。
EFAULT rwlp指向一个非法地址。
3.3.7清除读写锁
rwlock_destroy(3T)
#include
int rwlock_destroy(rwlock_t *rwlp);
使用rwlock_destroy()来取消由rwlp指向的读写锁的状态。存储读写锁的空间不被释放。
返回值--rw_destroy ()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 非法参数。
EFAULT rwlp指向一个非法地址。
示例3-14用一个银行帐户来演示读写锁。如果一个程序允许多个线程同时进行读操作,一个时刻只有一个写操作被允许。注意get_balance()函数通过锁来保证检查和储存操作是原子操作。
Code Example 3-14 读/写银行帐户
Rwlock_t account_lock;
Float checking_balance=100.0;
Float saving_balance=100.0;
… …
rwlock_init (&account_lock, 0, NULL);
… …
float get_balance(){
float bal;
rw_rdlock(&account_lock);
bal=checking_balance +saving_balance;
rw_unlock(&account_lock);
return(bal);
}
void tranfer_checking_to_savings(float amount) {
rw_wrlock(&account_lock);
checking_balance=checking_balance - amount;
savings_balance=savings_balance +amount;
rw_unlock(&account_lock);
}
3.4信号量(信号灯)
信号灯是E.W.Dijkstra在60年代晚期定义的程序结构。Dijkstra的模型是一个铁路上的操作:一段单线铁路在一个时刻只允许一列火车通过。
用一个信号灯来维护这段铁路。一列火车在进入单线铁路之前必须等待信号灯 的许可。如果一列火车进入这段轨道,信号灯改变状态,以防止其他火车进入。在 火车离开这段轨道时,必须将信号灯复原,使得其他火车得以进入。
在信号灯的计算机版本中,一个信号灯一般是一个整数,称之为信号量。一个 线程在被允许进行后对信号量做一个p操作。
P操作的字面意思是线程必须等到信号量的值为正(positive)才能继续进行, 进行前先给信号量减1。当做完相关的操作时(相当于离开铁轨),线程执行一个 v操作,即给信号量加1。这两个操作必须具有不可中断性,也叫不可分性,英文字 面为原子性(atomic),即他们不能被分成两个子操作,在子操作之间还可以插入 其它线程的其他操作,这些操作可能改变信号量。在P操作中,信号量的值在被减之 前一定要为正(使得信号量在被减1之后不会为负)。
在P操作或V操作当中,操作不会互相干扰。如果两个V操作要同时执行,则信号量的新值比原来大2。
记住P和V本身是什么意思已经不重要了,就象记住Dijkstra是荷兰人一样。但 是,如果引起了学者考证的兴趣,P代表prolagen,一个由proberen de verlagen演 变来的合成词,它的意思是"试图减"。V代表verhogen,它的意思是"增加"。这些在 Dijkstra的技术笔记EWD 74中提到过。
Sema_wait(3T)和sema_post(3T)分别对应Dijkstra的P和V操作, sema_trywait(3T)是P操作的一个可选的形式,在P操作不能执行时,线程不会阻塞, 而是立刻返回一个非零值。
有两种基本的信号量:二值信号量,其值只能是0或者1,和计数信号量,可以 是非负值。一个二值信号量在逻辑上相当于一个互斥锁。
然而,尽管并不强制,互斥锁应当被认为只能被拥有锁的线程释放,而"拥有信 号量的线程"这个概念是不存在的,任何线程都可以进行一个V操作 (或sema_post(3T))。
计数信号量的功能大概和与互斥锁合用的条件变量一样强大。在很多情况下, 采用信号量的程序比采用条件变量要简单一些(如下面的例子所示)。
然而,如果一个互斥锁和条件变量一起使用,有一个隐含的框架,程序的哪一 部分被保护是明显的。在信号量则不然,它可以用同时性编程当中的go to 来调用, 它更适合用于那些结构性不强的,不精确的方面。
3.4.1计数信号量
在概念上,一个信号量是一个非负整数。信号量在典型情况下用来协调资源, 信号量一般被初始化为可用资源的数量。线程在假如资源是给计数器加1,在拿走资 源时给计数器减1,操作都具有原子性。
如果一个信号量的值变为0,表明已无可用资源,想要给信号量减1的操作必须 等到它为正时。
表3-4 信号量函数
函数 操作
Sema_init(3T) 初始化信号量
Sema_post(3T) 增加信号量
Sema_wait(3T) 关于信号量阻塞
Sema_trywait(3T) 减少信号量
Sema_destroy(3T) 破坏信号量的状态
因为信号量不被哪个线程占有,它们可以用异步事件来通知(例如信号处理器)。 而且,因为信号量包含状态,他们可以被异步使用???,而不用象条件变量那样 一定要先获得互斥锁。
缺省情况下,等待信号量的多个线程退出阻塞的顺序是不确定的。
信号量在使用前一定要初始化。
3.4.2初始化一个信号量
sema_init(3T)
#include
int sema_init(sema_t *sp, unsigned int count, int type, void *arg);
sema_init用count的值来初始化由sp指向的信号量。Type可以是如下值之一(arg先不谈)。
USYNC_PROCESS 信号量可以在进程间进行线程同步。只有一个进程需要初始化
信号量。Arg忽略。
USYNC_THREAD 信号量只能在进程内部进行线程同步。
多个线程不能同时初始化同一个信号量。一个信号量在使用中不能被其他线程重新初始化。
返回值--sema_init()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 非法参数。
EFAULT sp或arg指向一个非法地址。
3.4.3给信号量增值
sema_post(3T)
#include
int sema_destroy(sema_t *sp);
用sema_post()给由sp指向的信号量原子地(表示其不可分性,下同)增1,如果有其它线程关于信号量阻塞,其中一个退出阻塞状态。
返回值--sema_post()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数

