C++ mutex库源码解析

1.1 互斥量类型 mutex

不支持赋值/移动拷贝;

1.1.1 std:mutex & timed_mutex

互斥量类,该类型有如下几个特性:

  • 当一个线程调用 m.lock 函数获取到他,只有本线程有资格释放他,其他线程就无法再获取或者释放,也就说即使他被定义为了全局对象,之后如果有一个线程对该mutex上锁,那么其他线程对他上锁就会被阻塞,对他解锁就会直接报出异常;

  • 该类型禁止拷贝或者移动拷贝;

  • 一个线程占有了一个mutex,但是在thread结束之前未对其解锁就会产生未定义行为;如果一个mutex对象被一个thread上锁,但是mutex对象被析构了而未对其解锁也会产生未定义行为;

class __mutex_base  {
protected:
    typedef pthread_mutex_t __native_type;
    __native_type  _M_mutex; // pthread_mutex_t;
    __mutex_base() noexcept
    {
        // XXX EAGAIN, ENOMEM, EPERM, EBUSY(may), EINVAL(may)
        __GTHREAD_MUTEX_INIT_FUNCTION(&_M_mutex);
    }
​
    ~__mutex_base() noexcept { __gthread_mutex_destroy(&_M_mutex); }
    #endif
​
    __mutex_base(const __mutex_base&) = delete;
    __mutex_base& operator=(const __mutex_base&) = delete;
};
​
  /// The standard mutex type.
class mutex : private __mutex_base{
public:
    typedef __native_type*          native_handle_type;
​
    constexpr mutex() noexcept = default;
    ~mutex() = default;
​
    mutex(const mutex&) = delete;
    mutex& operator=(const mutex&) = delete;
​
    void lock() {
        int __e = pthread_mutex_lock(&_M_mutex); // 系统调用;
          // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
        if (__e)
            __throw_system_error(__e);
    }
​
    bool try_lock() noexcept
    {
      // XXX EINVAL, EAGAIN, EBUSY
      return !pthread_mutex_trylock(&_M_mutex); // 系统调用;
    }
    void unlock(){
        // XXX EINVAL, EAGAIN, EPERM
        pthread_mutex_unlock(&_M_mutex); // 系统调用;
    }
​
    native_handle_type native_handle() noexcept{
        return &_M_mutex;
    }
};

带等待时间的互斥量:只是简单地比mutex多了几个带有等待时间的 lock函数而已;

// 比普通的 mutex类多继承了一个 __timed_mutex_impl模板类(里面定义了一些带有时间的上锁成员函数);
class timed_mutex: private __mutex_base, public __timed_mutex_impl<timed_mutex>
{
public:
    timed_mutex() = default;
    ~timed_mutex() = default;
    timed_mutex(const timed_mutex&) = delete;
    timed_mutex& operator=(const timed_mutex&) = delete;
    void lock(){
        int __e = pthread_mutex_lock(&_M_mutex);
        // EINVAL, EAGAIN, EBUSY, EINVAL, EDEADLK(may)
        if (__e)
            __throw_system_error(__e);
    }

    bool try_lock() noexcept {
        // XXX EINVAL, EAGAIN, EBUSY
        return !pthread_mutex_trylock(&_M_mutex);
    }
	// 尝试上锁阻塞一段相对时间,就是在内部调用基类的成员函数;
    template <class _Rep, class _Period>
    bool try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
    { return _M_try_lock_for(__rtime); }
	// 尝试上锁阻塞到一个时钟系统下的绝对时间点,就是在内部调用基类的成员函数;
    template <class _Clock, class _Duration>
    bool try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
    { return _M_try_lock_until(__atime); }
	// 解锁;
    void unlock(){
        // XXX EINVAL, EAGAIN, EBUSY
        pthread_mutex_unlock(&_M_mutex);
    }
	// 返回内部的pthread_mutex_t*;
    pthread_mutex_t* native_handle() noexcept
    { return &_M_mutex; }

private:
    friend class __timed_mutex_impl<timed_mutex>;
	// 阻塞等待一段相对时间,timespec是一个由 s 和 ns 两个成员组成的时间间隔结构体;
    bool _M_timedlock(const timespec& __ts)
    { return !pthread_mutex_timedlock(&_M_mutex, &__ts); }
	// 指定一个时钟系统,然后阻塞到该系统下的一个绝对时间点,这个绝对时间是通过从该时钟系统启动的纪元时间epoch到目标时间的时间间隔表示;clockid_t就是int类型表示的时间,表示一个时钟系统类型;
    bool _M_clocklock(clockid_t clockid, const timespec& __ts)
    { return !pthread_mutex_clocklock(&_M_mutex, clockid, &__ts); }
};

// 内部类型
template<typename _Derived>
class __timed_mutex_impl {
protected:
    template<typename _Rep, typename _Period>
    bool _M_try_lock_for(const chrono::duration<_Rep, _Period>& __rtime){
        using __clock = chrono::steady_clock;
        auto __rt = chrono::duration_cast<__clock::duration>(__rtime);
        if (ratio_greater<__clock::period, _Period>())
        ++__rt;
        return _M_try_lock_until(__clock::now() + __rt);
    }
	// 下面为不同时钟系统的try_lock_until函数;
    template<typename _Duration>
    bool _M_try_lock_until(const chrono::time_point<chrono::system_clock, _Duration>& __atime){
        auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
        auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
        __gthread_time_t __ts = {
            static_cast<std::time_t>(__s.time_since_epoch().count()),
            static_cast<long>(__ns.count())
        };
        return static_cast<_Derived*>(this)->_M_timedlock(__ts);
    }
    
    template<typename _Duration>
    bool _M_try_lock_until(const chrono::time_point<chrono::steady_clock, _Duration>& __atime){ ... } // 代码和上面一样,省略;

    template<typename _Clock, typename _Duration>
    bool _M_try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime){...}
};

 chrono时间库:

(1)duration类:chrono::duration是表示时间段的类型,是一段时间,其中第一个模板是用什么类型来表示一段时间,第二个模板是一段时间持续的秒数s,通过std::ratio有理数分数类型来表示,std::ratio也是一个模板类,他的第一个参数为分子,第二个参数为分母;

template<typename _Rep, typename _Period> // _Period需要通过ratio类来传入;
struct duration {
private:
    _Rep __r; // 表示时间总长的成员变量,粒度为 _Period秒;假如 __r=2,std::ratio<60,1>的话,这个duration实例对象表示的时间长度就是2s;count函数就是直接返回该对象;
public:
    using rep = _Rep;
    using period = typename _Period::type; // 这也是个ratio类,就是相当于给原来的ratio类起了一个别名而已;
    // ...
    // construction / copy / destroy都有;
    constexpr duration() = default;
    duration(const duration&) = default; // 默认拷贝构造;
    constexpr duration(const duration<_Rep2, _Period2>& __d) :__r(duration_cast<duration>(__d).count()) { } // 类型转换拷贝构造,当被用于初始化的duration的_Period和当前的不一样就会优先调用该函数;
    // ...一些加减乘除运算符重载;
    // 返回长度成员;
    constexpr rep count() const { return __r; }
};
​
// 有理数类型,用作表示duration的_Period模板参数;
template<intmax_t _Num, intmax_t _Den = 1>
struct ratio {
    ...
    // Note: sign(N) * abs(N) == N
    static constexpr intmax_t num =
    _Num * __static_sign<_Den>::value / __static_gcd<_Num, _Den>::value; // 分子;
​
    static constexpr intmax_t den =
    __static_abs<_Den>::value / __static_gcd<_Num, _Den>::value; // 分母;
​
    typedef ratio<num, den> type; // 表示该ratio类型调整后的别名,其实就是将本类型时间对齐之后的一个ratio类型;
};

我们还可以直接通过字面值来定义一个duration对象,因为该类型重载了几个不同单位的运算符:

// 下面看一下 ""s 运算符源码:
// 直接返回一个 std::chrono::seconds的 秒为单位的duration类型实例:
constexpr std::chrono::seconds operator""s(unsigned long long s)
{
    return std::chrono::seconds(s);
}

// 返回一个 未定义粒度的duration类型实例用于类型转换:
constexpr std::chrono::duration<long double> operator""s(long double s)
{
    return std::chrono::duration<long double>(s);
}

// 举例应用:
auto d2 = 2s; // 生成一个std::chrono::seconds类型表示的2s实例;
cout << d2.count() << endl; // 输出2;
auto d2 = 1min; // 生成一个std::chrono::minutes类型表示的1分钟的实例;
cout << d2.count() << endl; // 输出1;
std::chrono::duration<int, ratio<1>> d2 = 1min; // 先生成一个std::chrono::minutes类型表示的1分钟的实例,之后类型转换为std::chrono::seconds类型表示;
cout << d2.count() << endl; // 输出60;

其中在 std::chrono库 中已经预定义好了许多不同单位的duration类:

duration类型转换函数:可以通过duration_cast函数将一个时间粒度的duration转换成另一个粒度来表示;

template<typename _ToDur, typename _Rep, typename _Period>
constexpr duration<_Rep, _Period> duration_cast(const duration<_Rep, _Period>& __d) {
    typedef typename _ToDur::period             __to_period;
    typedef typename _ToDur::rep                __to_rep;
    typedef ratio_divide<_Period, __to_period>      __cf;
    typedef typename common_type<__to_rep, _Rep, intmax_t>::type
                                __cr;
    typedef  __duration_cast_impl<_ToDur, __cf, __cr,
                      __cf::num == 1, __cf::den == 1> __dc;
    return __dc::__cast(__d); // 转换;
}
// 下面为__duration_cast_impl类的类型转换成员函数;
template<typename _Rep, typename _Period>
static constexpr _ToDur __cast(const duration<_Rep, _Period>& __d) {
    typedef typename _ToDur::rep            __to_rep;
    return _ToDur(static_cast<__to_rep>(static_cast<_CR>(__d.count())
    * static_cast<_CR>(_CF::num)
    / static_cast<_CR>(_CF::den))); // 转换之后的duration类;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值