Linux线程同步之——条件变量

来源:互联网 发布:委托网络国债基金产品 编辑:程序博客网 时间:2024/06/06 14:01

  条件变量是线程可用的另一种同步机制。条件变量给多线程提供了一个会合的场所。它主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。

  条件变量本身是互斥量保护的。线程在改变条件状态之前必须首先锁住互斥量。其他线程在获得互斥量之前不会觉察到这种改变,因为互斥量必须在锁定以后才能计算条件。

  在使用条件变量之前,必须对它进行初始化。由pthread_cond_t数据类型表示的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,则需要使用pthread_cond_init函数对它进行初始化。

  在释放条件变量底层的内存空间之前,可以使用pthread_cond_destroy函数对条件变量进行反初始化。

#include <pthread.h>int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);int pthread_cond_destroy(pthread_cond_t *cond);两个函数的返回值:若成功,返回0;否则,返回错误编号
  通常情况下,pthread_cond_init函数的attr参数设置为NULL。

  我们使用pthread_cond_wait等待条件变量变为真。pthread_cond_wait函数与pthread_cond_wait函数相似,只是多了一个超时。超时值指定我们愿意等待多长的时间,它通过timespace结构指定,这个时间值是一个绝对数而不是相对数。如果超时到期时条件还是没有出现,pthread_coond_timewait将重新获取互斥量,然后返回错误ETIMEOUT。

#include <pthread.h>int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespace *restrict tsptr);两个函数的返回值:若成功,返回0;否则,返回错误编号

  有两个函数可以用于通知线程条件已经满足。pthread_cond_signal函数至少能唤醒一个等待该条件的线程,而pthread_cond_broadcast函数则能唤醒等待该条件的所有线程。

#include <pthread.h>int pthread_cond_signal(pthread_cont_t *cond);int pthread_cond_broadcast(pthread_cond_t *cond);两个函数的返回值:若成功,返回0;否则,返回错误编号

  下面,举一个在“线程池”中经常用到的任务队列,当我们将一个任务放入到任务队列的时候,可以通过条件变量告知等待线程,已经有新任务,可以来取任务执行或当某一线程需要取从任务队列取任务时候,如果任务队列为空,则线程挂起等待条件变量变为真。

  文件Mutext.h

#ifndef __MUTEX_H__#define __MUTEX_H__#include <pthread.h>namespace taskqueue{class CMutexLock{public:explicit CMutexLock(){pthread_mutex_init(&m_Mutex, NULL);}~CMutexLock(){pthread_mutex_destroy(&m_Mutex);}void lock(){pthread_mutex_lock(&m_Mutex);}void unlock(){pthread_mutex_unlock(&m_Mutex);}pthread_mutex_t *get(){return &m_Mutex;}private:friend class CCondition;pthread_mutex_t m_Mutex;};class CMutexLockPart{public:explicit CMutexLockPart(CMutexLock &MutexLock):m_MutexLock(MutexLock){m_MutexLock.lock();}~CMutexLockPart(){m_MutexLock.unlock();}private:CMutexLock &m_MutexLock;};}#endif//#ifndef __MUTEX_H__
  文件Condition.h

#ifndef __CONDITION_H__  #define __CONDITION_H__    #include "Mutex.h" #include <time.h>class CCondition  {  public:  explicit CCondition(CMutexLock &MutexLock):m_MutexLock(MutexLock)  {  ::pthread_cond_init(&m_Condition, NULL);}  ~CCondition()  {  ::pthread_cond_destroy(&m_Condition);  }  void wait()  {  ::pthread_cond_wait(&m_Condition, m_MutexLock.get());  }  bool waitForSeconds(int iSeconds){struct timespec abstime;::clock_gettime(CLOCK_REALTIME, &abstime);abstime.tv_sec += static_cast<time_t>(iSeconds);return ETIMEDOUT == ::pthread_cond_timedwait(&m_Condition, m_MutexLock.get(), &abstime);}void signal()  {  ::pthread_cond_signal(&m_Condition);}  void broadcast()  {  ::pthread_cond_broadcast(&m_Condition);  }  private:  CMutexLock &m_MutexLock;  pthread_cond_t m_Condition;  };#endif//#ifndef __CONDITION_H__  

  文件TaskQueue.h

#ifndef __TASK_QUEUE_H__#define __TASK_QUEUE_H__#include <queue>#include "Mutex.h"#include "Condition.h"namespace taskqueue{using namespace std;template<typename T>class CTaskQueue{public:explicit CTaskQueue():m_MutexLock(),m_NotEmpty(m_MutexLock),m_queue(){}~CTaskQueue(){}void PutTask(const T &task){CMutexLockPart PartLock(m_MutexLock);m_queue.push(task);m_NotEmpty.signal();//֪ͨÏ̱߳íʾÓÐÈÎÎñ¿ÉÖ´ÐÐ}T GetTask(){CMutexLockPart PartLock(m_MutexLock);if (m_queue.empty()){m_NotEmpty.wait();//ÈÎÎñ¶ÓÁÐΪ¿Õ£¬ÄÇô¾Í¹ÒÆðµÈ´ý}T RetTask =  m_queue.front();m_queue.pop();return RetTask;}private:CMutexLock m_MutexLock;CCondition m_NotEmpty;queue<T> m_queue;};}#endif//#ifndef __TASK_QUEUE_H__
  下面为具体的测试代码,该测试代码创建了2个线程,第一个线程负责每2秒向任务队列里面放任务,总共放5个任务;第二个线程负责从任务队列里取任务,并执行任务,共取5次。

#include <iostream>#include <unistd.h>#include <pthread.h>#include "TaskQueue.h"using namespace std;using namespace taskqueue;static int g_iGetTaskCount;typedef void(*TaskFunc)(void);void Task(void){g_iGetTaskCount++;cout << "Task Count: " << g_iGetTaskCount << endl;}void *FuncPutTask(void *pArg){CTaskQueue<TaskFunc> *pTaskQueue = static_cast<CTaskQueue<TaskFunc> *>(pArg);for (int i = 0; i < 5; i++){pTaskQueue->PutTask(Task);sleep(2);}pthread_exit((void *)1);}void *FuncGetTask(void *pArg){CTaskQueue<TaskFunc> *pTaskQueue = static_cast<CTaskQueue<TaskFunc> *>(pArg);TaskFunc Task;for (int i = 0; i < 5; i++){Task = pTaskQueue->GetTask();Task();}pthread_exit((void *)2);}/*在多线程编程中,我们经常用到"线程池",在线程池中我们经常使用任务队列。 *  我们的例子展示的是条件变量的一种使用方式:当我们将一个任务放入到任务队 *列的时候通过条件变量告知挂起线程;在取任务队列时候,如果任务队列为空 *  那么就等待任务的到来。 */int main(void){int iRet;pthread_t ThreadPutTaskId;pthread_t ThreadGetTaskId;CTaskQueue<TaskFunc> TaskQueue;iRet = pthread_create(&ThreadPutTaskId, NULL, FuncPutTask, &TaskQueue);if (iRet != 0){cout << "pthread_create:FuncPutTask false!" << endl;return -1;}iRet = pthread_create(&ThreadGetTaskId, NULL, FuncGetTask, &TaskQueue);if (iRet != 0){cout << "pthread_create:FuncGetTask false!" << endl;return -1;}pthread_join(ThreadPutTaskId, NULL);pthread_join(ThreadGetTaskId, NULL);return 0;}
  我们运行之后的效果为每2秒打印如下所示:

Task Count: 1Task Count: 2Task Count: 3Task Count: 4Task Count: 5

0 0
原创粉丝点击