打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
pthread_cond_wait().
pthread_cond_wait(). 收藏
    今天终于有点明白了,赶快记下心得!
    条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:
  extern int pthread_cond_init __P ((pthread_cond_t *__cond,__const pthread_condattr_t *__cond_attr));
  其中cond是一个指向结构pthread_cond_t的指针,cond_attr是一个指向结构pthread_condattr_t的指针。结构 pthread_condattr_t是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是 PTHREAD_ PROCESS_PRIVATE,即此条件变量被同一进程内的各个线程使用。注意初始化条件变量只有未被使用时才能重新初始化或被释放。释放一个条件变量的函数为pthread_cond_destroy(pthread_cond_t cond)。 
    也可以静态的初始化条件变量
     pthread_cond_t my_condition = PTHREAD_COND_INITIALIZER;
  函数pthread_cond_wait()使线程阻塞在一个条件变量上。它的函数原型为:
  extern int pthread_cond_wait __P ((pthread_cond_t *__cond,pthread_mutex_t *__mutex));
   调用这个函数时,线程解开mutex指向的锁并被条件变量cond阻塞。线程可以被函数pthread_cond_signal和函数 pthread_cond_broadcast唤醒线程被唤醒后,它将重新检查判断条件是否满足,如果还不满足,一般说来线程应该仍阻塞在这里,被等待被下一次唤醒。这个过程一般用while语句实现。
    通过下面的程序来理解:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /*初始化互斥锁*/
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;  //初始化条件变量
void *thread1(void *);
void *thread2(void *);
int i=1;
int main(void)
{
        pthread_t t_a;
        pthread_t t_b;
        pthread_create(&t_a,NULL,thread1,(void *)NULL);/*创建进程t_a*/
        pthread_create(&t_b,NULL,thread2,(void *)NULL); /*创建进程t_b*/
        pthread_join(t_b, NULL);/*等待进程t_b结束*/
        pthread_mutex_destroy(&mutex);
        pthread_cond_destroy(&cond);
         exit(0);
}
void *thread1(void *junk)
{
        for(i=1;i<=9;i++)
        {
             printf("IN one\n"); 
             pthread_mutex_lock(&mutex);//
             if(i%3==0)
                 pthread_cond_signal(&cond);/*条件改变,发送信号,通知t_b进程*/
              else      
                  printf("thead1:%d\n",i);
              pthread_mutex_unlock(&mutex);//*解锁互斥量*/
              printf("Up  Mutex\n");     
       sleep(10);
        }
}
void *thread2(void *junk)
{
        while(i<9)
        {
      
             printf("IN two \n"); 
              pthread_mutex_lock(&mutex);
            if(i%3!=0)
                   pthread_cond_wait(&cond,&mutex);/*等待*/
            printf("thread2:%d\n",i);
            pthread_mutex_unlock(&mutex);
            printf("Down  Mutex\n");
            sleep(10);
 }
}
----------------------------
IN one
thead1:1
Up  Mutex
IN two
IN one
thead1:2
Up  Mutex
IN one
Up  Mutex
thread2:3
Down  Mutex
IN one
thead1:4
Up  Mutex
IN two
IN one
thead1:5
Up  Mutex
IN one
Up  Mutex
thread2:6
Down  Mutex
IN one
thead1:7
Up  Mutex
IN two
IN one
thead1:8
Up  Mutex
IN one
Up  Mutex
thread2:9
Down  Mutex
-------
总结
pthread_cond_wait()是在当点  unlock 等待常量,其他线程就可以得到锁了,当得到signal时候会又会lock,,,,,继续往当点走,直到unlock,
加强----------
使用条件变量
本节介绍如何使用条件变量。表 4–6 列出了可用的函数。
表 4–6 条件变量函数  操作
 相关函数说明
 
初始化条件变量
 pthread_cond_init 语法
 
基于条件变量阻塞
 pthread_cond_wait 语法
 
解除阻塞特定线程
 pthread_cond_signal 语法
 
在指定的时间之前阻塞
 pthread_cond_timedwait 语法
 
在指定的时间间隔内阻塞
 pthread_cond_reltimedwait_np 语法
 
解除阻塞所有线程
 pthread_cond_broadcast 语法
 
销毁条件变量状态
 pthread_cond_destroy 语法
 
 
初始化条件变量
使用 pthread_cond_init(3C) 可以将 cv 所指示的条件变量初始化为其缺省值,或者指定已经使用 pthread_condattr_init() 设置的条件变量属性。

pthread_cond_init 语法
int pthread_cond_init(pthread_cond_t *cv,
    const pthread_condattr_t *cattr);
#include <pthread.h>
 
pthread_cond_t cv;
pthread_condattr_t cattr;
int ret;
 
/* initialize a condition variable to its default value */
ret = pthread_cond_init(&cv, NULL);
 
/* initialize a condition variable */
ret = pthread_cond_init(&cv, &cattr); cattr 设置为 NULL。将 cattr 设置为 NULL 与传递缺省条件变量属性对象的地址等效,但是没有内存开销。对于 Solaris 线程,请参见cond_init 语法。
使用 PTHREAD_COND_INITIALIZER 宏可以将以静态方式定义的条件变量初始化为其缺省属性。PTHREAD_COND_INITIALIZER 宏与动态分配具有 null 属性的 pthread_cond_init() 等效,但是不进行错误检查。
多个线程决不能同时初始化或重新初始化同一个条件变量。如果要重新初始化或销毁某个条件变量,则应用程序必须确保该条件变量未被使用。

pthread_cond_init 返回值
pthread_cond_init() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:
cattr 指定的值无效。
EBUSY
描述:
条件变量处于使用状态。
EAGAIN
描述:
必要的资源不可用。
ENOMEM
描述:
内存不足,无法初始化条件变量。

基于条件变量阻塞
使用 pthread_cond_wait(3C) 可以以原子方式释放 mp 所指向的互斥锁,并导致调用线程基于 cv 所指向的条件变量阻塞。对于 Solaris 线程,请参见cond_wait 语法。

pthread_cond_wait 语法
int pthread_cond_wait(pthread_cond_t *cv,pthread_mutex_t *mutex);
#include <pthread.h>
 
pthread_cond_t cv;
pthread_mutex_t mp;
int ret;
 
/* wait on condition variable */
ret = pthread_cond_wait(&cv, &mp); 阻塞的线程可以通过 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒,也可以在信号传送将其中断时唤醒。
不能通过 pthread_cond_wait() 的返回值来推断与条件变量相关联的条件的值的任何变化。必须重新评估此类条件。
pthread_cond_wait() 例程每次返回结果时调用线程都会锁定并且拥有互斥锁,即使返回错误时也是如此。
该条件获得信号之前,该函数一直被阻塞。该函数会在被阻塞之前以原子方式释放相关的互斥锁,并在返回之前以原子方式再次获取该互斥锁。
通常,对条件表达式的评估是在互斥锁的保护下进行的。如果条件表达式为假,线程会基于条件变量阻塞。然后,当该线程更改条件值时,另一个线程会针对条件变量发出信号。这种变化会导致所有等待该条件的线程解除阻塞并尝试再次获取互斥锁。
必须重新测试导致等待的条件,然后才能从 pthread_cond_wait() 处继续执行。唤醒的线程重新获取互斥锁并从 pthread_cond_wait() 返回之前,条件可能会发生变化。等待线程可能并未真正唤醒。建议使用的测试方法是,将条件检查编写为调用 pthread_cond_wait() 的 while() 循环。

    pthread_mutex_lock();
        while(condition_is_false)
            pthread_cond_wait();
    pthread_mutex_unlock();如果有多个线程基于该条件变量阻塞,则无法保证按特定的顺序获取互斥锁。

--------------------------------------------------------------------------------
注 –
pthread_cond_wait() 是取消点。如果取消处于暂挂状态,并且调用线程启用了取消功能,则该线程会终止,并在继续持有该锁的情况下开始执行清除处理程序。

--------------------------------------------------------------------------------

pthread_cond_wait 返回值
pthread_cond_wait() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
描述:
cv 或 mp 指定的值无效。

解除阻塞一个线程
对于基于 cv 所指向的条件变量阻塞的线程,使用 pthread_cond_signal(3C) 可以解除阻塞该线程。对于 Solaris 线程,请参见cond_signal 语法。

pthread_cond_signal 语法
int pthread_cond_signal(pthread_cond_t *cv);
#include <pthread.h>
 
pthread_cond_t cv;
int ret;
 
/* one condition variable is signaled */
ret = pthread_cond_signal(&cv); 应在互斥锁的保护下修改相关条件,该互斥锁用于获得信号的条件变量中。否则,可能在条件变量的测试和 pthread_cond_wait() 阻塞之间修改该变量,这会导致无限期等待。
调度策略可确定唤醒阻塞线程的顺序。对于 SCHED_OTHER,将按优先级顺序唤醒线程。
如果没有任何线程基于条件变量阻塞,则调用 pthread_cond_signal() 不起作用。
 
--------------------------------------------------------------------------------
示例 4–8 使用 pthread_cond_wait() 和 pthread_cond_signal()
 
pthread_mutex_t count_lock;
pthread_cond_t count_nonzero;
unsigned count;
 
decrement_count()
{
    pthread_mutex_lock(&count_lock);
    while (count == 0)
        pthread_cond_wait(&count_nonzero, &count_lock);
    count = count - 1;
    pthread_mutex_unlock(&count_lock);
}
 
increment_count()
{
    pthread_mutex_lock(&count_lock);
    if (count == 0)
        pthread_cond_signal(&count_nonzero);
    count = count + 1;
    pthread_mutex_unlock(&count_lock);
}
--------------------------------------------------------------------------------

pthread_cond_signal 返回值
pthread_cond_signal() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
描述:
cv 指向的地址非法。
示例 4–8 说明了如何使用 pthread_cond_wait() 和 pthread_cond_signal()。

在指定的时间之前阻塞
pthread_cond_timedwait(3C) 的用法与 pthread_cond_wait() 的用法基本相同,区别在于在由 abstime 指定的时间之后 pthread_cond_timedwait() 不再被阻塞。

pthread_cond_timedwait 语法
int pthread_cond_timedwait(pthread_cond_t *cv,
    pthread_mutex_t *mp, const struct timespec *abstime);
#include <pthread.h>
#include <time.h>
 
pthread_cond_t cv;
pthread_mutex_t mp;
timestruct_t abstime;
int ret;
 
/* wait on condition variable */
ret = pthread_cond_timedwait(&cv, &mp, &abstime); pthread_cond_timewait() 每次返回时调用线程都会锁定并且拥有互斥锁,即使 pthread_cond_timedwait() 返回错误时也是如此。 对于 Solaris 线程,请参见cond_timedwait 语法。
pthread_cond_timedwait() 函数会一直阻塞,直到该条件获得信号,或者最后一个参数所指定的时间已过为止。

--------------------------------------------------------------------------------
注 –
pthread_cond_timedwait() 也是取消点。

--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------

示例 4–9 计时条件等待
 
pthread_timestruc_t to;
pthread_mutex_t m;
pthread_cond_t c;
...
pthread_mutex_lock(&m);
to.tv_sec = time(NULL) + TIMEOUT;
to.tv_nsec = 0;
while (cond == FALSE) {
    err = pthread_cond_timedwait(&c, &m, &to);
    if (err == ETIMEDOUT) {
        /* timeout, do something */
        break;
    }
}
pthread_mutex_unlock(&m);
--------------------------------------------------------------------------------

pthread_cond_timedwait 返回值
pthread_cond_timedwait() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:
cv 或 abstime 指向的地址非法。
ETIMEDOUT
描述:
abstime 指定的时间已过。
超时会指定为当天时间,以便在不重新计算值的情况下高效地重新测试条件,如示例 4–9 中所示。

在指定的时间间隔内阻塞
pthread_cond_reltimedwait_np(3C) 的用法与 pthread_cond_timedwait() 的用法基本相同,唯一的区别在于 pthread_cond_reltimedwait_np() 会采用相对时间间隔而不是将来的绝对时间作为其最后一个参数的值。

pthread_cond_reltimedwait_np 语法
int  pthread_cond_reltimedwait_np(pthread_cond_t *cv,
    pthread_mutex_t *mp,
   const struct timespec *reltime);
#include <pthread.h>
#include <time.h>
 
pthread_cond_t cv;
pthread_mutex_t mp;
timestruct_t reltime;
int ret;
 
/* wait on condition variable */
ret = pthread_cond_reltimedwait_np(&cv, &mp, &reltime); pthread_cond_reltimedwait_np() 每次返回时调用线程都会锁定并且拥有互斥锁,即使 pthread_cond_reltimedwait_np() 返回错误时也是如此。对于 Solaris 线程,请参见 cond_reltimedwait(3C)。pthread_cond_reltimedwait_np() 函数会一直阻塞,直到该条件获得信号,或者最后一个参数指定的时间间隔已过为止。

--------------------------------------------------------------------------------
注 –
pthread_cond_reltimedwait_np() 也是取消点。

--------------------------------------------------------------------------------

pthread_cond_reltimedwait_np 返回值
pthread_cond_reltimedwait_np() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下任一情况,该函数将失败并返回对应的值。
EINVAL
描述:
cv 或 reltime 指示的地址非法。
ETIMEDOUT
描述:
reltime 指定的时间间隔已过。

解除阻塞所有线程
对于基于 cv 所指向的条件变量阻塞的线程,使用 pthread_cond_broadcast(3C) 可以解除阻塞所有这些线程,这由 pthread_cond_wait() 来指定。

pthread_cond_broadcast 语法
int pthread_cond_broadcast(pthread_cond_t *cv);
#include <pthread.h>
 
pthread_cond_t cv;
int ret;
 
/* all condition variables are signaled */
ret = pthread_cond_broadcast(&cv); 如果没有任何线程基于该条件变量阻塞,则调用 pthread_cond_broadcast() 不起作用。对于 Solaris 线程,请参见cond_broadcast 语法。
由于 pthread_cond_broadcast() 会导致所有基于该条件阻塞的线程再次争用互斥锁,因此请谨慎使用 pthread_cond_broadcast()。例如,通过使用 pthread_cond_broadcast(),线程可在资源释放后争用不同的资源量,如示例 4–10 中所示。
 
--------------------------------------------------------------------------------

示例 4–10 条件变量广播
 
pthread_mutex_t rsrc_lock;
pthread_cond_t rsrc_add;
unsigned int resources;
 
get_resources(int amount)
{
    pthread_mutex_lock(&rsrc_lock);
    while (resources < amount) {
        pthread_cond_wait(&rsrc_add, &rsrc_lock);
    }
    resources -= amount;
    pthread_mutex_unlock(&rsrc_lock);
}
 
add_resources(int amount)
{
    pthread_mutex_lock(&rsrc_lock);
    resources += amount;
    pthread_cond_broadcast(&rsrc_add);
    pthread_mutex_unlock(&rsrc_lock);
}
--------------------------------------------------------------------------------
请注意,在 add_resources() 中,首先更新 resources 还是首先在互斥锁中调用 pthread_cond_broadcast() 无关紧要。
应在互斥锁的保护下修改相关条件,该互斥锁用于获得信号的条件变量中。否则,可能在条件变量的测试和 pthread_cond_wait() 阻塞之间修改该变量,这会导致无限期等待。

pthread_cond_broadcast 返回值
pthread_cond_broadcast() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
描述:
cv 指示的地址非法。

销毁条件变量状态
使用 pthread_cond_destroy(3C) 可以销毁与 cv 所指向的条件变量相关联的任何状态。对于 Solaris 线程,请参见cond_destroy 语法。

pthread_cond_destroy 语法
int pthread_cond_destroy(pthread_cond_t *cv);
#include <pthread.h>
 
pthread_cond_t cv;
int ret;
 
/* Condition variable is destroyed */
ret = pthread_cond_destroy(&cv); 请注意,没有释放用来存储条件变量的空间。

pthread_cond_destroy 返回值
pthread_cond_destroy() 在成功完成之后会返回零。其他任何返回值都表示出现了错误。如果出现以下情况,该函数将失败并返回对应的值。
EINVAL
描述:
cv 指定的值无效。

唤醒丢失问题
如果线程未持有与条件相关联的互斥锁,则调用 pthread_cond_signal() 或 pthread_cond_broadcast() 会产生唤醒丢失错误。
满足以下所有条件时,即会出现唤醒丢失问题:

一个线程调用 pthread_cond_signal() 或 pthread_cond_broadcast()
另一个线程已经测试了该条件,但是尚未调用 pthread_cond_wait()
没有正在等待的线程
信号不起作用,因此将会丢失
仅当修改所测试的条件但未持有与之相关联的互斥锁时,才会出现此问题。只要仅在持有关联的互斥锁同时修改所测试的条件,即可调用 pthread_cond_signal() 和 pthread_cond_broadcast(),而无论这些函数是否持有关联的互斥锁。

生成方和使用者问题
并发编程中收集了许多标准的众所周知的问题,生成方和使用者问题只是其中的一个问题。此问题涉及到一个大小限定的缓冲区和两类线程(生成方和使用者),生成方将项放入缓冲区中,然后使用者从缓冲区中取走项。
生成方必须在缓冲区中有可用空间之后才能向其中放置内容。使用者必须在生成方向缓冲区中写入之后才能从中提取内容。
条件变量表示一个等待某个条件获得信号的线程队列。
示例 4–11 中包含两个此类队列。一个队列 (less) 针对生成方,用于等待缓冲区中出现空位置。另一个队列 (more) 针对使用者,用于等待从缓冲槽位的空位置中提取其中包含的信息。该示例中还包含一个互斥锁,因为描述该缓冲区的数据结构一次只能由一个线程访问。
 
--------------------------------------------------------------------------------
示例 4–11 生成方和使用者的条件变量问题
 
typedef struct {
    char buf[BSIZE];
    int occupied;
    int nextin;
    int nextout;
    pthread_mutex_t mutex;
    pthread_cond_t more;
    pthread_cond_t less;
} buffer_t;
 
buffer_t buffer;
--------------------------------------------------------------------------------
如示例 4–12 中所示,生成方线程获取该互斥锁以保护 buffer 数据结构,然后,缓冲区确定是否有空间可用于存放所生成的项。如果没有可用空间,生成方线程会调用 pthread_cond_wait()。pthread_cond_wait() 会导致生成方线程连接正在等待 less 条件获得信号的线程队列。less 表示缓冲区中的可用空间。
与此同时,在调用 pthread_cond_wait() 的过程中,该线程会释放互斥锁的锁定。正在等待的生成方线程依赖于使用者线程在条件为真时发出信号,如示例 4–12 中所示。该条件获得信号时,将会唤醒等待 less 的第一个线程。但是,该线程必须再次锁定互斥锁,然后才能从 pthread_cond_wait() 返回。
获取互斥锁可确保该线程再次以独占方式访问缓冲区的数据结构。该线程随后必须检查缓冲区中是否确实存在可用空间。如果空间可用,该线程会向下一个可用的空位置中进行写入。
与此同时,使用者线程可能正在等待项出现在缓冲区中。这些线程正在等待条件变量 more。刚在缓冲区中存储内容的生成方线程会调用 pthread_cond_signal() 以唤醒下一个正在等待的使用者。如果没有正在等待的使用者,此调用将不起作用。
最后,生成方线程会解除锁定互斥锁,从而允许其他线程处理缓冲区的数据结构。
 
--------------------------------------------------------------------------------
示例 4–12 生成方和使用者问题:生成方
 
void producer(buffer_t *b, char item)
{
    pthread_mutex_lock(&b->mutex);
  
    while (b->occupied >= BSIZE)
        pthread_cond_wait(&b->less, &b->mutex);
 
    assert(b->occupied < BSIZE);
 
    b->buf[b->nextin++] = item;
 
    b->nextin %= BSIZE;
    b->occupied++;
 
    /* now: either b->occupied < BSIZE and b->nextin is the index
       of the next empty slot in the buffer, or
       b->occupied == BSIZE and b->nextin is the index of the
       next (occupied) slot that will be emptied by a consumer
       (such as b->nextin == b->nextout) */
 
    pthread_cond_signal(&b->more);
 
    pthread_mutex_unlock(&b->mutex);
}
--------------------------------------------------------------------------------
请注意 assert() 语句的用法。除非在编译代码时定义了 NDEBUG,否则 assert() 在其参数的计算结果为真(非零)时将不执行任何操作。如果参数的计算结果为假(零),则该程序会中止。在多线程程序中,此类断言特别有用。如果断言失败,assert() 会立即指出运行时问题。assert() 还有另一个作用,即提供有用的注释。
以 /* now: either b->occupied ... 开头的注释最好以断言形式表示,但是由于语句过于复杂,无法用布尔值表达式来表示,因此将用英语表示。
断言和注释都是不变量的示例。这些不变量是逻辑语句,在程序正常执行时不应将其声明为假,除非是线程正在修改不变量中提到的一些程序变量时的短暂修改过程中。当然,只要有线程执行语句,断言就应当为真。
使用不变量是一种极为有用的方法。即使没有在程序文本中声明不变量,在分析程序时也应将其视为不变量。
每次线程执行包含注释的代码时,生成方代码中表示为注释的不变量始终为真。如果将此注释移到紧挨 mutex_unlock() 的后面,则注释不一定仍然为真。如果将此注释移到紧跟 assert() 之后的位置,则注释仍然为真。
因此,不变量可用于表示一个始终为真的属性,除非一个生成方或一个使用者正在更改缓冲区的状态。线程在互斥锁的保护下处理缓冲区时,该线程可能会暂时声明不变量为假。但是,一旦线程结束对缓冲区的操作,不变量即会恢复为真。
示例 4–13 给出了使用者的代码。该逻辑流程与生成方的逻辑流程相对称。
 
--------------------------------------------------------------------------------
示例 4–13 生成方和使用者问题:使用者
 
char consumer(buffer_t *b)
{
    char item;
    pthread_mutex_lock(&b->mutex);
    while(b->occupied <= 0)
        pthread_cond_wait(&b->more, &b->mutex);
 
    assert(b->occupied > 0);
 
    item = b->buf[b->nextout++];
    b->nextout %= BSIZE;
    b->occupied--;
 
    /* now: either b->occupied > 0 and b->nextout is the index
       of the next occupied slot in the buffer, or
       b->occupied == 0 and b->nextout is the index of the next
       (empty) slot that will be filled by a producer (such as
       b->nextout == b->nextin) */
 
    pthread_cond_signal(&b->less);
    pthread_mutex_unlock(&b->mutex);
 
    return(item);
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xiaochangfu/archive/2009/10/26/4729342.aspx
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
多线程互斥
pthread条件变量函数的使用
深入浅出 Barriers 实现(一)
互斥量、条件变量与pthread_cond_wait()函数的使用,详解
pthread_cond_wait() 前使用 while 讲解
Linux系统下的多线程编程入门-开发频道-多线程-天极网
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服