1醉蚁、當(dāng)析構(gòu)遇到多線程
? ? ? ? 1)即將析構(gòu)的一個(gè)對(duì)象是否有其他的線程正在執(zhí)行該對(duì)象的成員函數(shù)
? ? ? ? 2)該對(duì)象成員函數(shù)執(zhí)行期間,對(duì)象會(huì)不會(huì)另一個(gè)線程析構(gòu)
? ? ? ? 3)在調(diào)用成員函數(shù)之前鬼店,如何知道該對(duì)象還活著网棍,析構(gòu)會(huì)不會(huì)執(zhí)行到一半?
2妇智、線程安全定義
? ? ? ? 1)多個(gè)線程同時(shí)訪問(wèn)滥玷,表現(xiàn)出正確的行為
? ? ? ? 2)無(wú)論系統(tǒng)如何調(diào)用,無(wú)論線程如何交織
? ? ? ? 3)調(diào)用端代碼無(wú)序額外的同步或其他協(xié)調(diào)動(dòng)作
// A thread-safe counter
class Counter : boost::noncopyable
{
? // copy-ctor and assignment should be private by default for a class.
public:
? Counter() : value_(0) {}
? Counter& operator=(const Counter& rhs);
? int64_t value() const;
? int64_t getAndIncrease();
? friend void swap(Counter& a, Counter& b);
private:
? mutable MutexLock mutex_;
? int64_t value_;
};
int64_t Counter::value() const
{
? MutexLockGuard lock(mutex_);//lock 的析構(gòu)會(huì)晚于返回對(duì)象的構(gòu)造巍棱,
? return value_;? ? ? ? ? ? ? ? ? ? ? ? ? ? //因此有效地保護(hù)了這個(gè)共享數(shù)據(jù)惑畴。
}
int64_t Counter::getAndIncrease()
{
? MutexLockGuard lock(mutex_);
? int64_t ret = value_++;
? return ret;
}
//當(dāng)然在實(shí)際項(xiàng)目中,這個(gè)class用原子操作更合理航徙,這里用鎖僅僅為了舉例如贷。
3、對(duì)象構(gòu)造要線程安全到踏,唯一要求的是構(gòu)造期間不要泄露this指針
? ? ? ? 1)不要在構(gòu)造函數(shù)中注冊(cè)任何回調(diào)杠袱;
? ? ? ? 2)不要在構(gòu)造函數(shù)中把this傳給跨線程的對(duì)象;
? ? ? ? 3)即便構(gòu)造函數(shù)最后一行也不行窝稿。
//不要這么做
class Foo : public Observer
{
public:
? ? ? ? ? ? Foo(Observable* s)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? s->register_(this);? ? //非線程安全
? ? ? ? ? ? }
? ? ? ? virtual void update();
};
class Foo : public Observer
{
public:
? ? ? ? Foo(){}
? ? ? ? virtual void update();
? ? ? ? //在另一個(gè)函數(shù)中構(gòu)造之后執(zhí)行回調(diào)
? ? ? ? void observe(Observable* s)
? ? ? ? {
? ? ? ? ? ? ? ? s->register_(this);
? ? ? ? }
};
Foo* pFoo = new Foo;
Observable* s = getSubject();
pFoo->observe(s);//二段式構(gòu)造? 或者直接寫s->register_(pFoo);
野指針:未經(jīng)初始化的指針楣富。
空懸指針:指向已經(jīng)銷毀的對(duì)象或已經(jīng)回收的地址。
成員函數(shù)用來(lái)保護(hù)臨界區(qū)的互斥器本身必須是有效地伴榔,而析構(gòu)函數(shù)破壞了這一假設(shè)纹蝴,它把mutex成員變量銷毀掉庄萎。----以下代碼解釋此句。
Foo::~Foo()
{
? ? MutexLockGuard lock(mutex_);
? ? // free internal state (1)
}
void Foo::update()
{
? ? MutexLockGuard lock(mutex_);? ? //(2)
? ? //make use of internal state
}
此時(shí)A塘安、B兩個(gè)線程同時(shí)對(duì)對(duì)象x操作惨恭。
extern Foo* x;
//thread A
delete x;
x = NULL;
//thread B
if(x)
{
? ? x->update();
}
1、線程A執(zhí)行到了析構(gòu)函數(shù)(1)處耙旦,已經(jīng)持有了互斥鎖脱羡,即將繼續(xù)執(zhí)行下去。
2免都、線程B通過(guò)了if(x)檢測(cè)锉罐,阻塞在(2)處。
因?yàn)槲鰳?gòu)函數(shù)會(huì)把mutex_銷毀掉绕娘。
面向?qū)ο蟪绦蛟O(shè)計(jì)中脓规,對(duì)象三種關(guān)系:composition(組合/復(fù)合)、aggregation(關(guān)聯(lián)/聯(lián)系)险领、association(聚合)侨舆。
c++標(biāo)準(zhǔn)對(duì)構(gòu)造函數(shù)和析構(gòu)函數(shù)中調(diào)用虛函數(shù)的行為有明確規(guī)定,但是沒(méi)有考慮并發(fā)調(diào)用的情況绢陌。
share_ptr控制對(duì)象的生命周期挨下,share_ptr是強(qiáng)引用,只要有一個(gè)指向x的對(duì)象share_ptr存在脐湾,該x對(duì)象就不會(huì)析構(gòu)臭笆,當(dāng)指向?qū)ο髕的最后一個(gè)share_ptr析構(gòu)或reset()的時(shí)候,x保證會(huì)被銷毀秤掌。
weak_ptr不會(huì)控制對(duì)象的生命周期愁铺,但是它知道對(duì)象是否還活著,如果對(duì)象還活著闻鉴,可以提升為有效的share_ptr茵乱;如果對(duì)象死了,提升會(huì)失敗孟岛,返回一個(gè)空的share_ptr瓶竭。“提升/lock()”行為時(shí)線程安全的蚀苛。
share_ptr/weak_ptr的“計(jì)數(shù)”在主流平臺(tái)是原子操作在验,沒(méi)有用鎖,性能不俗堵未。
share_ptr/weak_ptr的線程安全級(jí)別與std::string和STL容器一樣腋舌。
c++可能出現(xiàn)的內(nèi)存問(wèn)題:
1、緩沖區(qū)溢出:用vector<char>/string或自己編寫B(tài)uffer class來(lái)管理緩沖區(qū)渗蟹,自動(dòng)記住用緩沖區(qū)的長(zhǎng)度块饺,并通過(guò)成員函數(shù)而不是裸指針來(lái)修改緩沖區(qū)赞辩。
2、空懸指針/野指針:用share_ptr/weak_ptr
3授艰、充分釋放:用scoped_ptr辨嗽,只在對(duì)象析構(gòu)的時(shí)候釋放一次。
4淮腾、內(nèi)存泄漏:用scoped_tr糟需,對(duì)象析構(gòu)的時(shí)候自動(dòng)釋放內(nèi)存。
5谷朝、不配對(duì)的new[]/delete:把new[]統(tǒng)統(tǒng)替換為vector/scoped_array洲押。
class Observable
{
?public:
??void?register_(boost::weak_ptr<Observer>?x);//參數(shù)類型可用const weak_ptr<Observer>&
??//?void?unregister(boost::weak_ptr<Observer>?x);
??void?notifyObservers()
??{
????muduo::MutexLockGuard?lock(mutex_);
????Iterator?it?=?observers_.begin();
????while?(it?!=?observers_.end())
????{
??????boost::shared_ptr<Observer>?obj(it->lock());//嘗試提升,這一步是線程安全的圆凰。
??????if?(obj)
??????{
? ? ? ? //提升成功杈帐,現(xiàn)在引用計(jì)數(shù)值至少為2
????????obj->update();//沒(méi)有竟態(tài)條件,因?yàn)閛bj在站上专钉,對(duì)象不肯在本作用域內(nèi)銷毀挑童。
????????++it;
??????}
??????else
??????{
????????printf("notifyObservers()?erase\n");
????????it?=?observers_.erase(it);? ? //對(duì)象已經(jīng)銷毀,從容器中拿掉weak_ptr
??????}
????}
??}
?private:
??mutable?muduo::MutexLock?mutex_;
??std::vector<boost::weak_ptr<Observer>?>?observers_;
??typedef?std::vector<boost::weak_ptr<Observer>?>::iterator?Iterator;
};
一個(gè)share_ptr對(duì)象實(shí)體可以被多個(gè)線程同時(shí)讀仍拘搿站叼;
兩個(gè)share_ptr對(duì)象實(shí)體可以被兩個(gè)線程同時(shí)寫入,“析構(gòu)”算寫操作回怜;
如果要從多個(gè)線程讀寫同一個(gè)share_ptr對(duì)象大年,那么需要加鎖。
在多個(gè)線程中同時(shí)訪問(wèn)同一個(gè)share_ptr正確用mutex保護(hù)做法:
MutexLock mutex;
share_ptr<Foo> globalPtr;
//我們的任務(wù)時(shí)把globalPTR安全的穿給doit();
void doit(const shared_ptr<Foo>& pFoo);
為了拷貝globalPtr,需要在讀取它的時(shí)候加鎖玉雾,
void read()
{
? ? shared_ptr<Foo> localPtr;
????{
? ? ? ? MutexLockGuard lock(mutex);
????????localPtr = globalPtr;? ? //read globalPtr
????}
? ? //use localPtr since here, 讀寫localPtr也無(wú)須加鎖。
? ? doit(localPtr);
? }
//寫時(shí)需要加鎖
void write()
{
shared_ptr<Foo>??newPtr(new Foo);? ? ? ? //對(duì)象的創(chuàng)建在臨界區(qū)之外
????{
MutexLockGuard lock(mutex);
globalPtr = newPtr;? ? //write to? globalPtr
????}
//usenewPtr since here, 讀寫newPtr也無(wú)須加鎖轻要。
doit(newPtr);
? }
namespace?version1
{
//?questionable?code
class?StockFactory?:?boost::noncopyable
{
?public:
??boost::shared_ptr<Stock>?get(const?string&?key)
??{
????muduo::MutexLockGuard?lock(mutex_);
????boost::shared_ptr<Stock>&?pStock?=?stocks_[key];
????if?(!pStock)
????{
??????pStock.reset(new?Stock(key));
????}
????return?pStock;
??}
?private:
??mutable?muduo::MutexLock?mutex_;
??std::map<string,?boost::shared_ptr<Stock>?>?stocks_;
};
}
namespace?version2
{
class?StockFactory?:?boost::noncopyable
{
?public:
??boost::shared_ptr<Stock>?get(const?string&?key)
??{
????boost::shared_ptr<Stock>?pStock;
????muduo::MutexLockGuard?lock(mutex_);
????boost::weak_ptr<Stock>&?wkStock?=?stocks_[key];
????pStock?=?wkStock.lock();
????if?(!pStock)
????{
??????pStock.reset(new?Stock(key));
??????wkStock?=?pStock;
????}
????return?pStock;
??}
?private:
??mutable?muduo::MutexLock?mutex_;
??std::map<string,?boost::weak_ptr<Stock>?>?stocks_;
};
}
namespace?version3
{
class?StockFactory?:?boost::noncopyable
{
?public:
??boost::shared_ptr<Stock>?get(const?string&?key)
??{
????boost::shared_ptr<Stock>?pStock;
????muduo::MutexLockGuard?lock(mutex_);
????boost::weak_ptr<Stock>&?wkStock?=?stocks_[key];
????pStock?=?wkStock.lock();
????if?(!pStock)
????{
??????pStock.reset(new?Stock(key),
???????????????????boost::bind(&StockFactory::deleteStock,?this,?_1));
??????wkStock?=?pStock;
????}
????return?pStock;
??}
?private:
??void?deleteStock(Stock*?stock)
??{
????printf("deleteStock[%p]\n",?stock);
????if?(stock)
????{
??????muduo::MutexLockGuard?lock(mutex_);
??????stocks_.erase(stock->key());??//?This?is?wrong,?see?removeStock?below?for?correct?implementation.
????}
????delete?stock;??//?sorry,?I?lied
??}
??mutable?muduo::MutexLock?mutex_;
??std::map<string,?boost::weak_ptr<Stock>?>?stocks_;
};
}
namespace?version4
{
class?StockFactory?:?public?boost::enable_shared_from_this<StockFactory>,
?????????????????????boost::noncopyable
{
?public:
??boost::shared_ptr<Stock>?get(const?string&?key)
??{
????boost::shared_ptr<Stock>?pStock;
????muduo::MutexLockGuard?lock(mutex_);
????boost::weak_ptr<Stock>&?wkStock?=?stocks_[key];
????pStock?=?wkStock.lock();
????if?(!pStock)
????{
??????pStock.reset(new?Stock(key),
???????????????????boost::bind(&StockFactory::deleteStock,
???????????????????????????????shared_from_this(),
???????????????????????????????_1));
??????wkStock?=?pStock;
????}
????return?pStock;
??}
?private:
??void?deleteStock(Stock*?stock)
??{
????printf("deleteStock[%p]\n",?stock);
????if?(stock)
????{
??????muduo::MutexLockGuard?lock(mutex_);
??????stocks_.erase(stock->key());??//?This?is?wrong,?see?removeStock?below?for?correct?implementation.
????}
????delete?stock;??//?sorry,?I?lied
??}
??mutable?muduo::MutexLock?mutex_;
??std::map<string,?boost::weak_ptr<Stock>?>?stocks_;
};
}