1. 程式人生 > >muduo_base程式碼剖析之Timestamp、AtomicIntegerT、Exception

muduo_base程式碼剖析之Timestamp、AtomicIntegerT、Exception

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類的實現

在這裡插入圖片描述