muduo_base代码剖析之Timestamp、AtomicIntegerT、Exception

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/84305800

Timestamp类封装

时间戳一般用来唯一地标识某一刻的时间,通常是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数
在这里插入图片描述

  1. Timestamp类继承自boost::less_than_comparable 模板类,只要实现 <,即可自动实现>,<=,>=
  2. 使用到了BOOST_STATIC_ASSERT,编译时断言;常见的assert是运行时断言,用一个小例子来理解BOOST_STATIC_ASSERT:
#include <boost/static_assert.hpp>

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

编译上述例子,在编译时就会报错
在这里插入图片描述
3. gmtime和gmtime_r函数
用法:
struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

gmtime(线程不安全的)是把日期和时间转换为格林威治(GMT)时间的函数。将参数timep 所指的time_t 结构中的信息转换成真实世界所使用的时间日期表示方法,然后将结果由结构tm返回。使用gmtime后要立即处理结果,否则返回的指针指向的内容可能会被覆盖。一个好的方法是使用gmtime_r(线程安全的),gmtime_r()函数功能与此相同,但是它可以将数据存储到用户提供的结构体中,由于使用了用户分配的内存,是不会出错的。

节选muduo源码中的使用示例:

string Timestamp::toFormattedString() const
{
  char buf[32] = {0};
  time_t seconds = static_cast<time_t>(microSecondsSinceEpoch_ / kMicroSecondsPerSecond);  // 秒数
  int microseconds = static_cast<int>(microSecondsSinceEpoch_ % kMicroSecondsPerSecond);
  struct tm tm_time;  // 微秒数

  // 把time_t结构中的信息转换成真实世界所使用的时间日期,存储在tm_time结构中
  gmtime_r(&seconds, &tm_time);

  // 格式化输出时间戳
  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);
  return buf;
}
  1. 函数参数采用值传递
    类对象作为参数传递并不一定采用引用传递更高效,这里采用值传递,是因为Timestamp类只包含一个类型为int64_t的数据成员microSecondsSinceEpoch_,所以我们可以把Timestamp对象看作是一个64位(8字节)的整数。参数传递的过程中,会把参数传递到一个8字节的寄存器中而不是传递到堆栈当中(在对应的64位平台),它的效率会更高。
//此处的timestamp是值传递
inline Timestamp addTime(Timestamp timestamp, double seconds)
{
  int64_t delta = static_cast<int64_t>(seconds * Timestamp::kMicroSecondsPerSecond);
  return Timestamp(timestamp.microSecondsSinceEpoch() + delta);
}
  1. 使用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,需要两步:
// 包含头文件:<inttypes.h>
// 定义宏:__STDC_FORMAT_MACROS,可以通过编译时加-D__STDC_FORMAT_MACROS,或者在包含文件之前定义这个宏。
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#undef __STDC_FORMAT_MACROS 

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

原子性操作

  1. 为什么需要原子性操作?
    分析x++操作的执行过程:①从内存中读x的值到寄存器中②对寄存器加1③把新值写回x所处的内存地址
    而,如果两个线程对一个变量进行++操作,可能会发生预想不到的结果!
    解决方案有两种:(1)对变量加锁。(2)使用原子性操作,可以将①②③看成一个整体。
    注意:锁的开销比原子性操作的开销大得多
  2. 原子性操作
    注意:使用这些原子性操作,编译时需要加上-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)

无锁队列的实现 http://coolshell.cn/articles/8239.html

volatile作用:作为指令关键字,确保本条指令不会因为编译器优化而省略,且要求每次直接读值。简单地说就是防止编译器对代码进行优化。(被volatile修饰的变量,系统总是重新从它所在的内存中读取数据,而不是勇士保存在寄存器中的备份)

整数原子类AtomicIntegerT的封装

不使用加锁的方式,实现int类型原子化操作,避免多线程访问出现数据不一致的情况
在这里插入图片描述


Exception类的实现

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/84305800
今日推荐