1. 程式人生 > >理解 shared_ptr實現copy-on-write(COW)

理解 shared_ptr實現copy-on-write(COW)

shared_ptr實現COW(Copy-On-Write)

前不久在《Linux多執行緒服務端程式設計使用muduoC++網路庫》2.8節看到這個內容,一直沒有真正理解,後來在書中7.3中再次提到使用shared_ptr實現copy-on-write的手法降低鎖競爭,從shared_ptr的層面,徹底理解了一番。

1.如果你是資料的唯一擁有者,那麼你可以直接修改資料。
2.如果你不是資料的唯一擁有者,那麼你拷貝它之後再修改。

用shared_ptr來實現COW時,主要考慮兩點:
1.讀資料
2.寫資料

shared_ptr擁有對物件的引用計數,在對物件進行讀寫操作時,這個計數是1,當讀資料時,我們建立一個新的智慧指標指向原指標,這個時候引用計數加1。

//假設g_ptr是一個全域性的shared_ptr<Foo>並且已經初始化。
void read()
{
    shared_ptr<Foo> tmpptr;
    {
        lock();
        tmpptr=g_ptr;//此時引用計數為2,通過gdb除錯可以看到
    }
    //訪問tmpptr
    //...
}

這部分是shared_ptr最基本的用法,還是很好理解的,read()函式呼叫結束,tmpptr作為棧上變數離開作用域,自然析構,原資料物件的引用計數也變為1。

寫資料就複雜一些。根據COW的準則,當你是唯一擁有者(對應物件的引用計數是1)時,那麼你直接修改資料,這樣沒有問題,當你不是唯一擁有者,則需要拷貝資料再去修改,這就需要用到一些shared_ptr的程式設計技法了:

void write()
{
    lock()
    if(!g_ptr.unique())
    {
        g_ptr.reset(new Foo(*g_ptr));
    }
    assert(g_ptr.unique());
    //write
    //
}

解釋一下程式碼:
shared_ptr::unique(),當引用計數為1時返回true,否則false。
那麼當引用計數不為1的時候,說明有別的執行緒正在讀,受shread_ptr::reset()中example的誤導,一直以為,reset後,原物件被析構,這樣不就會影響正在讀的執行緒了嗎?

實際上:
假設一個執行緒讀,一個執行緒寫,當寫執行緒進入到if迴圈中時,原物件的引用計數為2,分別為tmpptrg_ptr,此時reset()函式將原物件的引用計數減1,並且g_ptr已經指向了新的物件(用原物件構造),這樣就完成了資料的拷貝,並且原物件還在,只是引用計數變成了1。