理解 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,分別為tmpptr
和g_ptr
,此時reset()
函式將原物件的引用計數減1,並且g_ptr
已經指向了新的物件(用原物件構造),這樣就完成了資料的拷貝,並且原物件還在,只是引用計數變成了1。