条件变量是线程可用的另一种同步机制。条件变量给多线程提供了一个会合的场所。它主要包括两个动作:一个线程等待“条件变量的条件成立”而挂起;另一个线程使“条件成立”(给出条件成立信号)。条件变量与互斥量一起使用时,允许线程以无竞争的方式等待特定的条件发生。
条件变量本身是互斥量保护的。线程在改变条件状态之前必须首先锁住互斥量。其他线程在获得互斥量之前不会觉察到这种改变,因为互斥量必须在锁定以后才能计算条件。
在使用条件变量之前,必须对它进行初始化。由pthread_cond_t数据类型表示的条件变量可以用两种方式进行初始化,可以把常量PTHREAD_COND_INITIALIZER赋给静态分配的条件变量,但是如果条件变量是动态分配的,则需要使用pthread_cond_init函数对它进行初始化。
在释放条件变量底层的内存空间之前,可以使用pthread_cond_destroy函数对条件变量进行反初始化。
#include
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
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
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
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
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(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
#include "Mutex.h"
#include "Condition.h"
namespace taskqueue
{
using namespace std;
template
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 m_queue;
};
}
#endif //#ifndef __TASK_QUEUE_H__
下面为具体的测试代码,该测试代码创建了2个线程,第一个线程负责每2秒向任务队列里面放任务,总共放5个任务;第二个线程负责从任务队列里取任务,并执行任务,共取5次。
#include
#include
#include
#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 *pTaskQueue = static_cast *>(pArg);
for (int i = 0; i < 5; i++)
{
pTaskQueue->PutTask(Task);
sleep(2);
}
pthread_exit((void *)1);
}
void *FuncGetTask(void *pArg)
{
CTaskQueue *pTaskQueue = static_cast *>(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 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: 1
Task Count: 2
Task Count: 3
Task Count: 4
Task Count: 5