2011年6月5日星期日

  理解pthread_cond_wait()

pthread_cond_wait() 所做的第一件事就是同时对互斥对象解锁(于是其它线程可以修改已链接列表),并等待条件 mycond 发生(这样当 pthread_cond_wait() 接收到另一个线程的“信号”时,它将苏醒)。现在互斥对象已被解锁,其它线程可以访问和修改已链接列表,可能还会添加项。 【要求解锁并阻塞是一个原子操作
此时,pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生,但等待条件 mycond 通常是一个阻塞操作,这意味着线程将睡眠,在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将一直睡眠,直到特定条件发生,在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看,它只是在等待 pthread_cond_wait() 调用返回。
现在继续说明,假设另一个线程(称作“2 号线程”)锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后,2 号线程会立即调用函数 pthread_cond_broadcast(&mycond)。此操作之后,2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程(仍处于 pthread_cond_wait() 调用中)现在将苏醒
现在,看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(&mymutex) 之后,1 号线程的 pthread_cond_wait() 会立即返回。不是那样!实际上,pthread_cond_wait() 将执行最后一个操作:重新锁定 mymutex。一旦 pthread_cond_wait() 锁定了互斥对象,那么它将返回并允许 1 号线程继续执行。那时,它可以马上检查列表,查看它所感兴趣的更改。

   1:  #include 
  
   2:  #include "error.c"
   3:   
   4:  struct msg {
   5:      struct msg *m_next;
   6:      /* ... more stuff here ... */
   7:  };
   8:   
   9:  struct msg *workq;
  10:  pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
  11:  pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
  12:   
  13:  void
  14:  process_msg(void)
  15:  {
  16:      struct msg *mp;
  17:   
  18:      for (;;) {
  19:          pthread_mutex_lock(&qlock);
  20:          while (workq == NULL)
  21:              pthread_cond_wait(&qready, &qlock); /*block-->unlock-->wait() return-->lock*/
  22:          mp = workq;
  23:          workq = mp->m_next;
  24:          pthread_mutex_unlock(&qlock);
  25:          /* now process the message mp */
  26:      }
  27:  }
  28:   
  29:  void
  30:  enqueue_msg(struct msg *mp)
  31:  {
  32:      pthread_mutex_lock(&qlock);
  33:      mp->m_next = workq;
  34:      workq = mp;
  35:      pthread_mutex_unlock(&qlock);
  36:      pthread_cond_signal(&qready);
  37:  }
  38:   

 

上面的例子中,遵循了如下步骤:

1:对互斥量加锁(pthread_mutex_lock)。

2:改变互斥量保护的条件。

3:对互斥量解锁(pthread_mutex_unlock)。

4:向等待条件的线程发送信号(pthread_cond_broadcast)。

在上面的步骤中,第3步和第4步之间获取互斥锁,然后使条件失效,最后释放互斥锁;接着,当调用pthread_cond_broadcast时,条件不再为真,线程无需运行。这就是为什么唤醒线程必须重新检查条件,不能仅仅因为pthread_cond_wait返回就假定条件为真。

没有评论:

发表评论