本文对Linux中的pthread_mutex_t做一个简易的封装。
互斥锁主要用于互斥,互斥是一种竞争关系,主要是某一个系统资源或一段代码,一次做多被一个线程访问。
条件变量主要用于同步,用于协调线程之间的关系,是一种合作关系。
Linux中互斥锁的用法很简单,最常用的是以下的几个函数:
//线程初始化
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
//上锁
int pthread_mutex_lock(pthread_mutex_t *mutex);
//尝试上锁
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
//销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
//返回:
//pthread_mutex_init总是返回0
//其他mutex函数返回0表示成功,非0的错误码表示失败
由于pthread系列函数返回成功的时候都是0,因此,我们可以写一个宏作为一个轻量级的检查手段,来判断处理错误。
/*
继续符 \
可用于一行的结尾,表示本行与下一行连接起来
C语言中以 ; 作为语句的结束,不以行为单位结束,当一行的内容太长不方便卸载一行时可使用反斜 杠"\"作为继续符
*/
#define CHECK(exp) \
if(!exp) \
{ \
fprintf(stderr, "File:%s, Line:%d Exp:[" #exp "] is true, abort.\n",__FILE__, __LINE__); abort();\
}
实际使用的时候只需:
CHECK(!pthread_mutex_lock(&mutex));
需要考虑以下几个问题:
a.互斥锁的初始化与销毁。
b.互斥锁的操作:加锁和释放锁。
这里完成了Thread 类的声明,但是这里还需要一些补充,那就是使用RAII(资源获取即初始化)技术,对MutexLock初始化和析构进行处理:初始化的时候加锁,析构的时候解锁,这就需要我们重新定义一个class MutexLockGuard
对MutexLock
进行操作
//CThreadMutex.h
class MutexLockGuard:public boost::noncopyable
{
public:
MutexLockGuard(CThreadMutex &mutex):mutex_(mutex){ mutex_.Lock();}//构造时加锁
~MutexLockGuard()//析构时解锁
{
mutex_.Unlock();
}
private:
CThreadMutex &mutex_;
};
另外,我们的自己封装的类不应该有赋值和拷贝构造的语义,这一点跟单例模式类似,我们可以使我们的类继承自boost库的noncopyable。
下面就要具体实现几个函数了,主要是:
pthread_mutex_init()
、pthread_mutex_destroy()
、pthread_mutex_lock()
、pthread_mutex_unlock()
//CThreadMutex.h
#pragma once
#include
#include
#include
#include
#include
#include
/*
继续符 \
可用于一行的结尾,表示本行与下一行连接起来
C语言中以 ; 作为语句的结束,不以行为单位结束,当一行的内容太长不方便卸载一行时可使用反斜 杠"\"作为继续符
*/
#define CHECK(exp) \
if(!exp) \
{ \
fprintf(stderr, "File:%s, Line:%d Exp:[" #exp "] is true, abort.\n",__FILE__, __LINE__); abort();\
}
class CThreadMutex : public boost::noncopyable
{
friend class Condition;//条件变量友元声明
public:
//查看是否上锁
bool isLocking() const
{
return m_isLocking;
}
//获得锁
pthread_mutex_t* getMutexPtr()
{
return &m_mutex;
}
//构造函数 创建锁 调用 pthread_mutex_init
CThreadMutex() :m_isLocking(false)
{
CHECK(!pthread_mutex_init(&m_mutex, 0));
}
//析构函数 锁销毁 调用pthread_mutex_destroy
~CThreadMutex()
{
assert(!isLocking());
CHECK(!pthread_mutex_destroy(&m_mutex));
}
//上锁 pthread_mutex_lock
void Lock()
{
CHECK(!pthread_mutex_lock(&m_mutex));//先加锁再修改状态,保证以下赋值操作的原子性。
m_isLocking = true;
}
//解锁 调用pthread_mutex_unlock
void Unlock()
{
m_isLocking = false;//先修改状态在解锁,保证赋值操作的原子性
CHECK(!pthread_mutex_unlock(&m_mutex));
}
private:
void restoreMutexStatus()
{
m_isLocking = true;
}
//锁
pthread_mutex_t m_mutex;
//是否上锁标志位
bool m_isLocking;
};
class MutexLockGuard:public boost::noncopyable
{
public:
MutexLockGuard(CThreadMutex &mutex):mutex_(mutex){ mutex_.Lock();}//构造时加锁
~MutexLockGuard()//析构时解锁
{
mutex_.Unlock();
}
private:
CThreadMutex &mutex_;
};
封装以后,我们使用:
MutexLockGurad lock(mutex);
对临界区进行加锁,而只要我们控制好lock变量的生存期,就能控制临界区,例如:
int count=0;
{
MutexLockGurad lock(mutex);
count++;
}//临界区
//...
//离开lock的作用域,lock作为栈上变量,自动释放,调用析构函数,同时释放锁。
C++封装POSIX 线程库(一)互斥锁的封装