Solaris2.4 多线程编程指南3--使用同步对象编程
情况发生时,函数失败并返回相关值。
EFAULT cvp指向一个非法地址。
EINTR 等待被信号或fork()中断。
3.2.3使指定线程退出阻塞状态
cond_signal(3T)
#include(or #include )
int cond_signal (cond_t *cvp);
用cond_signal()使得关于由cvp指向的条件变量阻塞的线程退出阻塞状态。在 同一个互斥锁的保护下使用cond_signal()。否则,条件变量可以在对关联条件变量 的测试和cond_wait()带来的阻塞之间获得信号,这将导致无限期的等待。
如果没有一个线程关于条件变量阻塞,cond_signal无效。
返回值--cond_signal()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EFAULT cvp指向一个非法地址。
Code Example 3-8 使用cond_wait(3T)和cond_signal(3T)的例子
Mutex_t count_lock;
Cond_t count_nonzero;
Unsigned int count;
Decrement_count()
{
mutex_lock(&count_lock);
while(count==0)
cond_wait(&count_nonzero,&count_lock);
count=count-1;
mutex_unlock(&count_lock);
}
increment_count()
{
mutex_lock(&count_lock);
if(count==0)
cond_signal(&count_nonzero);
count=count+1;
mutex_unlock(&count_lock);
}
3.2.4阻塞直到指定事件发生
cond_timedwait(3T)
#include(or #include )
int cond_timedwait(cond_t *cvp, mutex_t *mp,
timestruc_t *abstime);
cond_timedwait()和cond_wait()用法相似,差别在于cond_timedwait()在经过有abstime指定的时间时不阻塞。
即使是返回错误,cond_timedwait()也只在给互斥锁加锁后返回。
Cond_timedwait()函数阻塞,直到条件变量获得信号或者经过由abstime指定 的时间。Time-out被指定为一天中的某个时间,这样条件可以在不重新计算 time-out值的情况下被有效地重新测试,???就象在示例3-9中那样。
返回值--cond_timedwait()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 由abstime 指定的时间大于应用程序启动的时间加50,000,000,或者纳秒数大于等于1,000,000,000。
EFAULT cvp指向一个非法地址。
EINTR 等待被信号或fork()中断。
ETIME abstime指定的时间已过。
Code Example 3-9 时间条件等待
Timestruc_t to;
Mutex_t m;
Cond_t c;
Mutex_lock(&m);
To.tv_sec=time(NULL)+TIMEOUT;
To.tv_nsec=0;
While (cond==FALSE){
Err=cond_timedwait(&c,&m,&to);
If(err=ETIME) {
/* TIMEOUT, do something */
break;
}
}
mutex_unlock(&m);
3.2.5使所有线程退出阻塞状态
cond_broadcast(3T)
#include( or #include )
int cond_wait(cond_t *cvp);
用cond_broadcast()使得所有关于由cvp指向的条件变量阻塞的线程退出阻塞状态。如果没有阻塞的线程,cond_broadcast()无效。
这个函数唤醒所有由cond_wait()阻塞的线程。因为所有关于条件变量阻塞的线程都同时参与竞争,所以使用这个函数需要小心。
例如,用cond_broadcast()使得线程竞争变量资源,如示例3-10所示。
Code Example 3-10 条件变量广播
Mutex_t rsrc_lock;
Cond_t rsrc_add;
Unsigned int resources;
Get_resources(int amount)
{ mutex_lock(&rsrc_lock);
while(resources < amount) {
cond_wait(&rsrc_add, &rsrc_lock);
}
resources-=amount;
mutex_unlock(&rsrc_lock);
}
add_resources(int amount)
{
mutex_lock(&rsrc_lock);
resources +=amount;
cond_broadcast(&rsrc_add);
mutex_unlock(&rsrc_lock);
}
注意,在互斥锁的保护内部,首先调用cond_broadcast()或者首先给resource增值,效果是一样的。
返回值--cond_broadcast()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EFAULT cvp指向一个非法地址。
在互斥锁的保护下调用cond_broadcast()。否则,条件变量可能在检验关联状态和通过cond_wait()之间获得信号,这将导致永久等待。
3.2.6清除条件变量
cond_destroy(3T)
#include( or #include )
int cond_destroy(cond_t *cvp);
使用cond_destroy() 破坏由cvp指向的条件变量的任何状态。但是储存条件变量的空间将不被释放。
返回值--cond_destroy()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EFAULT cvp指向一个非法地址。
3.2.7唤醒丢失问题
在没有互斥锁保护的情况下调用cond_signal()或者cond_broadcast()会导致丢 失唤醒问题。一个唤醒丢失发生在信号或广播已经发出,但是线程即使在条件为真 时仍然关于条件变量阻塞,具体地说,这发生在调用cond_signal()时并没有获得互 斥锁的情况下。
如果一个线程已经作过条件检验,但是尚未调用cond_wait(),这时另外一个线 程调用cond_signal(),因为没有已被阻塞的线程,唤醒信号丢失。
3.2.8生产者/消费者问题
这个问题是一个标准的、著名的同时性编程问题的集合:一个有限缓冲区和两类线程,生产者和消费者,他们分别把产品放入缓冲区和从缓冲区中拿走产品。
一个生产者在缓冲区满时必须等待,消费者在缓冲区空时必须等待。
一个条件变量代表了一个等待条件的线程队列。
示例3-11有两个队列,一个(less)给生产者,它们等待空的位置以便放入信 息;另外一个(more)给消费者,它们等待信息放入缓冲区。这个例子也有一个互 斥锁,它是一个结构,保证同时只有一个线程可以访问缓冲区。
下面是缓冲区数据结构的代码。
Code Example 3-11 生产者/消费者问题和条件变量
Typedef struct{
Char buf[BSIZE];
Int occupled;
Int nextin;
Int nextout;
Mutex_t mutex;
Cond_t more;
Cond_t less;
}buffer_t;
buffer_t buffer;
如示例3-12所示,生产者用一个互斥锁保护缓冲区数据结构然后确定有足够的空 间来存放信息。如果没有,它调用cond_wait(),加入关于条件变量less阻塞的线程 队列,说明缓冲区已满。这个队列需要被信号唤醒。
同时,作为cond_wait()的一部分,线程释放互斥锁。等待的生产者线程依赖于 消费者线程来唤醒。当条件变量获得信号,等待less的线程队列里的第一个线程被唤 醒。但是,在线程从cond_wait()返回前,必须获得互斥锁。
这再次保证了线程获得对缓冲区的唯一访问权。线程一定要检测缓冲区有足够的 空间,如果有的话,它把信息放入下一
EFAULT cvp指向一个非法地址。
EINTR 等待被信号或fork()中断。
3.2.3使指定线程退出阻塞状态
cond_signal(3T)
#include
int cond_signal (cond_t *cvp);
用cond_signal()使得关于由cvp指向的条件变量阻塞的线程退出阻塞状态。在 同一个互斥锁的保护下使用cond_signal()。否则,条件变量可以在对关联条件变量 的测试和cond_wait()带来的阻塞之间获得信号,这将导致无限期的等待。
如果没有一个线程关于条件变量阻塞,cond_signal无效。
返回值--cond_signal()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EFAULT cvp指向一个非法地址。
Code Example 3-8 使用cond_wait(3T)和cond_signal(3T)的例子
Mutex_t count_lock;
Cond_t count_nonzero;
Unsigned int count;
Decrement_count()
{
mutex_lock(&count_lock);
while(count==0)
cond_wait(&count_nonzero,&count_lock);
count=count-1;
mutex_unlock(&count_lock);
}
increment_count()
{
mutex_lock(&count_lock);
if(count==0)
cond_signal(&count_nonzero);
count=count+1;
mutex_unlock(&count_lock);
}
3.2.4阻塞直到指定事件发生
cond_timedwait(3T)
#include
int cond_timedwait(cond_t *cvp, mutex_t *mp,
timestruc_t *abstime);
cond_timedwait()和cond_wait()用法相似,差别在于cond_timedwait()在经过有abstime指定的时间时不阻塞。
即使是返回错误,cond_timedwait()也只在给互斥锁加锁后返回。
Cond_timedwait()函数阻塞,直到条件变量获得信号或者经过由abstime指定 的时间。Time-out被指定为一天中的某个时间,这样条件可以在不重新计算 time-out值的情况下被有效地重新测试,???就象在示例3-9中那样。
返回值--cond_timedwait()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EINVAL 由abstime 指定的时间大于应用程序启动的时间加50,000,000,或者纳秒数大于等于1,000,000,000。
EFAULT cvp指向一个非法地址。
EINTR 等待被信号或fork()中断。
ETIME abstime指定的时间已过。
Code Example 3-9 时间条件等待
Timestruc_t to;
Mutex_t m;
Cond_t c;
Mutex_lock(&m);
To.tv_sec=time(NULL)+TIMEOUT;
To.tv_nsec=0;
While (cond==FALSE){
Err=cond_timedwait(&c,&m,&to);
If(err=ETIME) {
/* TIMEOUT, do something */
break;
}
}
mutex_unlock(&m);
3.2.5使所有线程退出阻塞状态
cond_broadcast(3T)
#include
int cond_wait(cond_t *cvp);
用cond_broadcast()使得所有关于由cvp指向的条件变量阻塞的线程退出阻塞状态。如果没有阻塞的线程,cond_broadcast()无效。
这个函数唤醒所有由cond_wait()阻塞的线程。因为所有关于条件变量阻塞的线程都同时参与竞争,所以使用这个函数需要小心。
例如,用cond_broadcast()使得线程竞争变量资源,如示例3-10所示。
Code Example 3-10 条件变量广播
Mutex_t rsrc_lock;
Cond_t rsrc_add;
Unsigned int resources;
Get_resources(int amount)
{ mutex_lock(&rsrc_lock);
while(resources < amount) {
cond_wait(&rsrc_add, &rsrc_lock);
}
resources-=amount;
mutex_unlock(&rsrc_lock);
}
add_resources(int amount)
{
mutex_lock(&rsrc_lock);
resources +=amount;
cond_broadcast(&rsrc_add);
mutex_unlock(&rsrc_lock);
}
注意,在互斥锁的保护内部,首先调用cond_broadcast()或者首先给resource增值,效果是一样的。
返回值--cond_broadcast()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EFAULT cvp指向一个非法地址。
在互斥锁的保护下调用cond_broadcast()。否则,条件变量可能在检验关联状态和通过cond_wait()之间获得信号,这将导致永久等待。
3.2.6清除条件变量
cond_destroy(3T)
#include
int cond_destroy(cond_t *cvp);
使用cond_destroy() 破坏由cvp指向的条件变量的任何状态。但是储存条件变量的空间将不被释放。
返回值--cond_destroy()在成功执行后返回零。其他值意味着错误。在以下情况发生时,函数失败并返回相关值。
EFAULT cvp指向一个非法地址。
3.2.7唤醒丢失问题
在没有互斥锁保护的情况下调用cond_signal()或者cond_broadcast()会导致丢 失唤醒问题。一个唤醒丢失发生在信号或广播已经发出,但是线程即使在条件为真 时仍然关于条件变量阻塞,具体地说,这发生在调用cond_signal()时并没有获得互 斥锁的情况下。
如果一个线程已经作过条件检验,但是尚未调用cond_wait(),这时另外一个线 程调用cond_signal(),因为没有已被阻塞的线程,唤醒信号丢失。
3.2.8生产者/消费者问题
这个问题是一个标准的、著名的同时性编程问题的集合:一个有限缓冲区和两类线程,生产者和消费者,他们分别把产品放入缓冲区和从缓冲区中拿走产品。
一个生产者在缓冲区满时必须等待,消费者在缓冲区空时必须等待。
一个条件变量代表了一个等待条件的线程队列。
示例3-11有两个队列,一个(less)给生产者,它们等待空的位置以便放入信 息;另外一个(more)给消费者,它们等待信息放入缓冲区。这个例子也有一个互 斥锁,它是一个结构,保证同时只有一个线程可以访问缓冲区。
下面是缓冲区数据结构的代码。
Code Example 3-11 生产者/消费者问题和条件变量
Typedef struct{
Char buf[BSIZE];
Int occupled;
Int nextin;
Int nextout;
Mutex_t mutex;
Cond_t more;
Cond_t less;
}buffer_t;
buffer_t buffer;
如示例3-12所示,生产者用一个互斥锁保护缓冲区数据结构然后确定有足够的空 间来存放信息。如果没有,它调用cond_wait(),加入关于条件变量less阻塞的线程 队列,说明缓冲区已满。这个队列需要被信号唤醒。
同时,作为cond_wait()的一部分,线程释放互斥锁。等待的生产者线程依赖于 消费者线程来唤醒。当条件变量获得信号,等待less的线程队列里的第一个线程被唤 醒。但是,在线程从cond_wait()返回前,必须获得互斥锁。
这再次保证了线程获得对缓冲区的唯一访问权。线程一定要检测缓冲区有足够的 空间,如果有的话,它把信息放入下一

