問題描述
在項目某次開發(fā)中睡榆,測試過程中出現(xiàn)了coredump問題综芥。經(jīng)過asan工具檢測牵囤,報了heap-use-after-free
內(nèi)存錯誤斗塘,最終定位到竟是無意中添加了一個引用&
導(dǎo)致的!
開發(fā)時因?yàn)榭吹较嚓P(guān)類訪問類成員的接口函數(shù)未返回引用止剖,而是返回了一個拷貝腺阳,因此想著要將返回值改為引用,避免多余的拷貝穿香。例如在以下代碼中亭引,我們將GetArr
函數(shù)的返回值改為引用:
class Foo {
private:
std::vector<int> arr;
public:
std::vector<int>& GetArr() { // add & for return type
return arr;
}
// other functions
};
看起來這并沒什么問題,但是在多線程環(huán)境下扔水,加一個&
可能就會導(dǎo)致程序崩潰痛侍。
問題分析
比如以下代碼展示了一個簡單的例子,表面看起來GetArr
是有鎖保護(hù)的魔市,但實(shí)際上鎖保護(hù)被打破了:
class Foo {
private:
std::vector<int> arr;
std::mutex mut;
public:
std::vector<int>& GetArr() {
std::lock_gurad<std::mutex> lk(mut);
return arr;
}
// other functions
};
為什么呢?GetArr
返回一個 vector 的引用赵哲,然后立即釋放鎖待德。因此,返回的引用可以在沒有任何保護(hù)的情況下被修改枫夺。當(dāng)多個線程對同一個Foo實(shí)例的arr進(jìn)行讀寫時将宪,就會導(dǎo)致數(shù)據(jù)競爭,從而導(dǎo)致程序崩潰的風(fēng)險橡庞。
如何避免较坛?
對于類對象的多線程共享數(shù)據(jù),接口應(yīng)返回拷貝而不是引用扒最。
std::vector<int> GetArr() { // return a copy
return arr;
}
總結(jié)
再翻翻《C++并發(fā)實(shí)戰(zhàn)》丑勤,才發(fā)現(xiàn)書中早已經(jīng)總結(jié)了這個陷阱:
通常來說,類的所有成員函數(shù)在訪問任何數(shù)據(jù)成員之前吧趣,假如都先對互斥加鎖法竞,并在完成訪問后解鎖,共享數(shù)據(jù)就可很好地受到全方位保護(hù)强挫〔戆裕可惜事與愿違:如果類的成員函數(shù)返回指向共享數(shù)據(jù)的指針或引用,那么這些數(shù)據(jù)就會在鎖外被訪問俯渤,從而破壞了互斥的效果呆细。
因此,我們可以總結(jié)一個簡單的規(guī)則:不得向鎖所在的作用域之外傳遞指針和引用八匠,指向受保護(hù)的共享數(shù)據(jù)絮爷,無論是通過函數(shù)返回值將它們保存到對外可見的內(nèi)存趴酣,還是將它們作為參數(shù)傳遞給使用者提供的函數(shù)。
參考
- C++ Concurrency in Action, Anthony Williams
你好略水,我是七昂价卤,致力于分享C++、計算機(jī)底層渊涝、機(jī)器學(xué)習(xí)等系列知識慎璧。希望我們能一起探索程序員修煉之道。如果我的創(chuàng)作內(nèi)容對您有幫助跨释,請點(diǎn)贊關(guān)注胸私。如果有問題,歡迎隨時與我交流鳖谈。感謝你的閱讀岁疼。