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

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

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

  在使用条件变量之前,必须对它进行初始化。由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

你可能感兴趣的:(Linux,多线程,c++,linux)