Linux多线程——封装POSIX线程库(一)锁封装

Linux多线程——封装POSIX线程库(一)锁封装

1.互斥锁简介   

本文对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));

2.互斥锁的封装

需要考虑以下几个问题:

a.互斥锁的初始化与销毁。 
b.互斥锁的操作:加锁和释放锁。

这里完成了Thread 类的声明,但是这里还需要一些补充,那就是使用RAII(资源获取即初始化)技术,对MutexLock初始化和析构进行处理:初始化的时候加锁,析构的时候解锁,这就需要我们重新定义一个class MutexLockGuardMutexLock进行操作

//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()

扫描二维码关注公众号,回复: 11143424 查看本文章

完整代码如下

//CThreadMutex.h
#pragma once
#include <iostream>
#include <cstdio>
#include <boost/noncopyable.hpp>
#include <pthread.h>
#include <assert.h>
#include <string.h>
/*
    继续符 \

   可用于一行的结尾,表示本行与下一行连接起来

  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_;
};

关于#include <boost/noncopyable.hpp> 

这个输入boost库内可以参考Boost库安装与使用

封装以后,我们使用:

MutexLockGurad lock(mutex);

对临界区进行加锁,而只要我们控制好lock变量的生存期,就能控制临界区,例如: 

int count=0;
{
    MutexLockGurad lock(mutex);
    count++;
}//临界区
//...
//离开lock的作用域,lock作为栈上变量,自动释放,调用析构函数,同时释放锁。

3.参考

C++封装POSIX 线程库(一)互斥锁的封装

原创文章 11 获赞 9 访问量 312

猜你喜欢

转载自blog.csdn.net/qq_41868108/article/details/105751303