muduo網路庫學習之Timestamp類、AtomicIntegerT 類封裝中的知識點

s1mba發表於2013-10-24

一、Timestamp類封裝

class Timestamp : public muduo::copyable,
                  public boost::less_than_comparable<Timestamp>
類圖如下:


值語義:可以拷貝,拷貝之後,與原物件脫離關係
物件語義:要麼是不能拷貝;要麼可以拷貝,拷貝之後與原物件仍然存在一定的關係,比如共享底層資源
兩者之間的關係參見這裡

1、muduo::copyable 空基類,標識類,值型別

2、less_than_comparable<Timestamp> 模板類
Timestamp 要求實現<, =,可自動實現>,<=,>=

3、成員 microSecondsSinceEpoch_ 是現在這個時刻距離1970-01-01 00:00:00 (UTC 時間) 的微秒數

4、BOOST_STATIC_ASSERT 編譯時斷言
     assert 執行時斷言

5、使用PRld64

int64_t 用來表示64位整數,在32位系統中是long long int,在64位系統中是long int,所以列印int64_t的格式化方法是:
printf(“%ld”, value);  // 64bit OS
printf("%lld", value); // 32bit OS

跨平臺的做法:
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#undef __STDC_FORMAT_MACROS
printf("%" PRId64 "\n", value);  

二、AtomicIntegerT 類封裝

template<typename T>
class AtomicIntegerT : boost::noncopyable

類圖如下:


1、為什麼需要原子性操作?

x++; (x是共享變數)
從記憶體中讀x的值到暫存器中,對暫存器加1,再把新值寫回x所處的記憶體地址

(1)、假設是多核(multiprocessors)的情況,x 的初始值為5:


因為每個核都有自己獨立的暫存器,兩個執行緒同時訪問可能出現只加了一次的情況。


(2)、假設是單核的情況:

例如有兩個執行緒,它們按照如下順序執行(注意讀x和寫回x是原子操作,兩個執行緒不能同時執行(偽併發),是分時實現):


time    Thread 1         Thread 2
      load eax, x
                           load eax, x
      add eax, 1        

     add eax, 1

     store x, eax
                           store x, eax


我們會發現最終x的值會是1而不是2,Thread 1的結果被覆蓋掉了,因為Thread2在進行++之前以為x還是0,而不是1(每個執行緒都儲存自己的上下文包括暫存器的值,重新排程回Thread2時eax被載入為原來儲存的0)。


這種情況下我們就需要對x++這樣的操作加鎖(例如Pthread中的mutex)以保證同步,或者使用一些提供了atomic operations的庫,linux下的如atomic.h,下面會介紹部分函式。


2、gcc 原子性操作

// 原子自增操作
type __sync_fetch_and_add (type *ptr, type value)

 type tmp = *ptr;
 *ptr += value;
   return tmp;

// 原子比較和設定操作
type __sync_val_compare_and_swap (type *ptr, type oldval, type newval)

 if (*ptr == oldval)  
{
*ptr = newval
return oldval;
}
else
     return *ptr;

bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval)

if (*ptr == oldval)  
{
*ptr = newval;
 return true;
}
else
     return false;


// 原子賦值操作
type __sync_lock_test_and_set (type *ptr, type value)

 type tmp = *ptr;
 *ptr = value;
   return tmp;


使用這些原子性操作,編譯的時候需要加-march=cpu-type  或者 -march = native




3、volatile 關鍵字

volatile的作用: 作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,且要求每次直接讀值。簡單地說就是防止編譯器對程式碼進行優化。當要求使用volatile 宣告的變數的值的時候,系統總是重新從它所在的記憶體讀取資料,而不是使用儲存在暫存器中的備份。即使它前面的指令剛剛從該處讀取過資料,而且讀取的資料立刻被儲存。


補充:gcc/g++ 一些編譯選項

-Wall               // 大部分警告
-Wextra               // 一些額外的警告
-Werror               // 當出現警告時轉為錯誤,停止編譯。(對應 -Wno-error)
-Wconversion          // 一些可能改變值的隱式轉換,給出警告。
-Wno-unused-parameter     // 函式中出現未使用的引數,不給出警告。
-Wold-style-cast          // C風格的轉換,給出警告
-Woverloaded-virtual     // 如果函式的宣告隱藏住了基類的虛擬函式,就給出警告。
-Wpointer-arith          // 對函式指標或者void *型別的指標進行算術操作時給出警告
-Wshadow          // 當一個區域性變數遮蓋住了另一個區域性變數,或者全域性變數時,給出警告。
-Wwrite-strings        // 規定字串常量的型別是const char[length],因此,把這樣的地址複製給 non-const char *指標將產生警告.     
                                   這些警告能夠幫助你在編譯期間發現企圖寫入字串常量 的程式碼
-march=native          // 指定cpu體系結構為本地平臺


注:本文對原子操作與鎖、volatile等討論比較基礎,更詳細的探討請參考這篇文章

參考:
muduo manual.pdf
《linux 多執行緒伺服器程式設計:使用muduo c++網路庫》

相關文章