muduo_base代码剖析之Timestamp、AtomicIntegerT、Exception

一、Timestamp类封装

  1. 时间戳一般用来唯一地标识某一刻的时间,通常是指格林威治时间1970年01月01日00时00分00秒起至现在的总微秒数
  2. Timestamp类使用的是微秒数,即用微秒数构建Timestamp对象。因此需要将微秒数转换成大家比较熟悉的秒数

muduo_base代码剖析之Timestamp、AtomicIntegerT、Exception_第1张图片

class Timestamp : public muduo::copyable,
                  public boost::equality_comparable<Timestamp>,
                  public boost::less_than_comparable<Timestamp>
{
public:
  Timestamp()
    : microSecondsSinceEpoch_(0) //到现在的总微秒数
  {
  }
  explicit Timestamp(int64_t microSecondsSinceEpochArg)
    : microSecondsSinceEpoch_(microSecondsSinceEpochArg)
  {
  }

  void swap(Timestamp& that)
  {
    std::swap(microSecondsSinceEpoch_, that.microSecondsSinceEpoch_);
  }

  //判断时间是否有效
  bool valid() const { return microSecondsSinceEpoch_ > 0; }
  //返回一个无效的时间
  static Timestamp invalid()
  {
    return Timestamp();
  }
  
  //从1970-01-01 00:00:00到现在的的微秒数
  int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }
  //从1970-01-01 00:00:00到现在的的秒数
  time_t secondsSinceEpoch() const 
  { 
    //microSecondsSinceEpoch_:从1970-01-01 00:00:00到现在的的微秒数
	//kMicroSecondsPerSecond:每秒所对应的微秒数1000 * 1000
    return static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); 
  }
  static Timestamp now()
  {
    struct timeval tv;
    gettimeofday(&tv, NULL); //获取当前的时间
    int64_t seconds = tv.tv_sec;
    //                秒数   *    每秒对应的微秒数     +  微秒数
    //用微秒数构造Timestamp对象,并返回
    return Timestamp(seconds * kMicroSecondsPerSecond + tv.tv_usec);
  }
  static Timestamp fromUnixTime(time_t t)
  {
    return fromUnixTime(t, 0);
  }
  static Timestamp fromUnixTime(time_t t, int microseconds)
  {
    return Timestamp(static_cast<int64_t>(t) * kMicroSecondsPerSecond + microseconds);
  }
  
  //转换成字符串格式:秒.微秒
  string toString() const 
  {
    char buf[32] = {0};
    int64_t seconds = microSecondsSinceEpoch_ / kMicroSecondsPerSecond; //秒
    int64_t microseconds = microSecondsSinceEpoch_ % kMicroSecondsPerSecond; //微秒
    snprintf(buf, sizeof(buf)-1, "%" PRId64 ".%06" PRId64 "", seconds, microseconds);
    return buf;
  }

  //转换成指定格式
  //   年月日 时:分:秒   20180905 08:45:56
  string toFormattedString(bool showMicroseconds = true) const
  {
    char buf[64] = {0};
    time_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); //秒
    struct tm tm_time;
    gmtime_r(&seconds, &tm_time); //将日历时间seconds转换为用UTC时间表示的时间struct tm tm_time

    if (showMicroseconds) //显示微秒
    {
      int microseconds = static_cast<int>(microSecondsSinceEpoch_ % kMicroSecondsPerSecond);
      snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d.%06d",
             tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
             tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec,
             microseconds);
    }
    else //不显示微秒
    {
      snprintf(buf, sizeof(buf), "%4d%02d%02d %02d:%02d:%02d",
             tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
             tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
    }
    return buf;
  }
  
  static const int kMicroSecondsPerSecond = 1000 * 1000;//每秒所对应的微秒数
private:
  //唯一的成员变量:表示1970年01月01日00时00分00秒起至现在的总微秒数
  int64_t microSecondsSinceEpoch_; 
};

两个全局函数:timeDifference、addTime

//计算时间差:high-low,单位:秒
inline double timeDifference(Timestamp high, Timestamp low)
{
  int64_t diff = high.microSecondsSinceEpoch() - low.microSecondsSinceEpoch();
  return static_cast<double>(diff) / Timestamp::kMicroSecondsPerSecond;
}
//将Timestamp类型的变量,加上seconds秒
inline Timestamp addTime(Timestamp timestamp, double seconds)//此处的timestamp是值传递
{
  //   总微秒数 =                       秒数   * 每秒所对应的微秒数,即1000*1000
  int64_t delta = static_cast<int64_t>(seconds * Timestamp::kMicroSecondsPerSecond);
  //先转换成微秒:timestamp.microSecondsSinceEpoch() + delta
  //再用微妙构建时间对象Timestamp
  return Timestamp(timestamp.microSecondsSinceEpoch() + delta);
}
  1. 使用到了BOOST_STATIC_ASSERT,编译时断言;常见的assert是运行时断言,用一个小例子来理解BOOST_STATIC_ASSERT:
#include 

class Timestamp
{
private: 
    int64_t microSecondsSinceEpoch_;
};

// 编译时断言通过
BOOST_STATIC_ASSERT(sizeof(Timestamp) == sizeof(int64_t));

// 编译时断言失败
BOOST_STATIC_ASSERT(sizeof(int) == sizeof(short));

int main(void)
{
    return 0;
}

编译上述例子,在编译时就会报错
在这里插入图片描述

  1. 函数参数采用值传递
    类对象作为参数传递并不一定采用引用传递更高效,这里采用值传递,是因为Timestamp类只包含一个类型为int64_t的数据成员microSecondsSinceEpoch_,所以我们可以把Timestamp对象看作是一个64位(8字节)的整数。参数传递的过程中,会把参数传递到一个8字节的寄存器中而不是传递到堆栈当中(在对应的64位平台),它的效率会更高。
  2. 使用PRId64
    int64_t用来表示64位整数,在32位系统中是long long int,在64位系统中是long int,所以打印int64_t的格式化方法是:
printf("%ld",value);  //64bit OS
printf("%lld",value);  //32bit OS

上面的方法是不可以移植的,跨平台的做法:

// C++使用PRID64,需要两步:
// 包含头文件:
// 定义宏:__STDC_FORMAT_MACROS,可以通过编译时加-D__STDC_FORMAT_MACROS,或者在包含文件之前定义这个宏。
#define __STDC_FORMAT_MACROS
#include 
#undef __STDC_FORMAT_MACROS 

printf("%" PRId64 "\n", value);

二、整数原子类AtomicIntegerT的封装

  1. volatile作用:作为指令关键字,确保本条指令不会因为编译器优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化。(被volatile修饰的变量,系统总是重新从它所在的内存中读取数据,而不是勇士保存在寄存器中的备份)
  2. 为什么需要原子性操作?
    分析x++操作的执行过程:①从内存中读x的值到寄存器中②对寄存器加1③把新值写回x所处的内存地址
    而,如果两个线程对一个变量进行++操作,可能会发生预想不到的结果!
    解决方案有两种:(1)对变量加锁。(2)使用原子性操作,可以将①②③看成一个整体。
    注意:锁的开销比原子性操作的开销大得多
  3. 原子性操作
    注意:使用这些原子性操作,编译时需要加上-march=cpu-type
原子自增操作 *ptr+value
type __sync_fetch_and_add(type* ptr,type value)
返回值:没有加value之前的值

原子比较和(设置)操作
type __sync_val_compare_and_swap(type* ptr,type oldval, type newval)
先比较,再设置:if (*ptr == oldval),则*ptr = newval
返回值:返回原来的值oldval

bool __sync_bool_compare_and_swap(type* ptr,type oldval, type newval)
返回值:if (*ptr == oldval){*ptr=newval; 返回true}
       else {不进行设置; 返回false;}

原子赋值操作 *ptr=value
type __sync_lock_test_and_set(type* ptr,type value)
  1. 无锁队列的实现http://coolshell.cn/articles/8239.html

  2. AtomicIntegerT类图
    muduo_base代码剖析之Timestamp、AtomicIntegerT、Exception_第2张图片


三、Exception类的实现

muduo_base代码剖析之Timestamp、AtomicIntegerT、Exception_第3张图片

你可能感兴趣的:(Muduo库源码剖析)