线程安全的 Counter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| #include <cinttypes> #include <mutex>
class noncopyable { public: noncopyable(const noncopyable&) = delete; void operator=(const noncopyable&) = delete;
protected: noncopyable() = default; ~noncopyable() = default; };
class Counter : public noncopyable { public: Counter() : value_(0) {} int64_t value() const; int64_t getAndIncrease();
private: int64_t value_; mutable std::mutex mutex_; };
int64_t Counter::value() const { std::lock_guard<std::mutex> lock(mutex_); return value_; }
int64_t Counter::getAndIncrease() { std::lock_guard<std::mutex> lock(mutex_); int64_t ret = value_++; return ret; }
|
此处有一点需要注意,为了使成员函数 Counter::value()
是 const 成员函数,且 mutex_
在语义上不可变,故将其设为 mutable
的。
对象构造期线程安全
对象构造要实现线程安全,唯一要求是不要在构造期间泄露 this
指针:
- 不要在构造函数中注册回调;
- 不要在构造函数中将
this
传给跨线程的对象;
- 即便在构造函数最后一行也不行。
错误
1 2 3 4 5 6 7 8
| class Foo : public Observer { public: Foo(Observable* s) { s->register_(this); }
virtual void update(); };
|
正确
1 2 3 4 5 6 7 8 9 10
| class Foo : public Observer { public: Foo();
void observer(Observable* s) { s->register_(this); }
virtual void update(); };
|
shared_ptr
亮点
析构动作在创建时被捕获
这意味着:
对象池优化过程
1. std::shared_ptr<Stock>
in map
1 2 3 4 5 6 7 8 9 10
| class Stock {};
class StockFactory : public noncopyable { public: std::shared_ptr<Stock> get(const std::string& key);
private: mutable std::mutex mutex_; std::map<std::string, std::shared_ptr<Stock>> stocks_; };
|
但是在 StockFactory
销毁前,Stock
对象永远不会销毁,毕竟逻辑上 Stock
本是可以在 StockFactory
销毁前销毁的,此处约等于内存泄漏。
2. std::weak_ptr<Stock>
in map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Stock { public: Stock(const std::string& key) : key_(key) { }
private: std::string key_; };
class StockFactory : public noncopyable { public: std::shared_ptr<Stock> get(const std::string& key);
private: mutable std::mutex mutex_; std::map<std::string, std::weak_ptr<Stock>> stocks_; };
std::shared_ptr<Stock> StockFactory::get(const std::string& key) { std::lock_guard<std::mutex> lock(mutex_); std::weak_ptr<Stock>& wkStock = stocks_[key]; std::shared_ptr<Stock> pStock = wkStock.lock(); if (pStock == nullptr) { pStock.reset(new Stock(key)); wkStock = pStock; } return pStock; }
|
stocks_.size
只增不减,可认为有内存泄漏。解决办法是,利用 std::shared_ptr
的定制 deleter 功能。
3. 定制 deleter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| std::shared_ptr<Stock> StockFactory::get(const std::string& key) { std::lock_guard<std::mutex> lock(mutex_); std::weak_ptr<Stock>& wkStock = stocks_[key]; std::shared_ptr<Stock> pStock = wkStock.lock(); if (pStock == nullptr) { pStock.reset(new Stock(key), [this](Stock* stock) { this->deleteStock(stock); }); wkStock = pStock; } return pStock; }
void StockFactory::deleteStock(Stock* stock) { if (stock) { std::lock_guard<std::mutex> lock(mutex_); stocks_.erase(stock->key()); }
delete stock; }
|
但是,此处将 StockFactory
的 this
指针交给了 Stock
析构函数。如果 StockFactory
先于 Stock
析构,就会 core dump。
4. enable_shared_from_this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class StockFactory : public std::enable_shared_from_this<StockFactory>, public noncopyable { ... };
std::shared_ptr<Stock> StockFactory::get(const std::string& key) { std::lock_guard<std::mutex> lock(mutex_); std::weak_ptr<Stock>& wkStock = stocks_[key]; std::shared_ptr<Stock> pStock = wkStock.lock(); if (pStock == nullptr) { pStock.reset(new Stock(key), [shared_this = shared_from_this()](Stock* stock) { shared_this->deleteStock(stock); }); wkStock = pStock; } return pStock; }
void StockFactory::deleteStock(Stock* stock) { if (stock) { std::lock_guard<std::mutex> lock(mutex_); stocks_.erase(stock->key()); }
delete stock; }
|
但是这样一来,StockFactory
的生命周期被意外延长了。
我们实际想做的是——如果 StockFactory
还活着,就调用它的成员函数,否则忽略之。
5. 弱引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| class StockFactory : public std::enable_shared_from_this<StockFactory>, public noncopyable { public: std::shared_ptr<Stock> get(const std::string& key);
private: static void weakDeleteStock(const std::weak_ptr<StockFactory>& wkFactory, Stock* stock); void removeStock(Stock* stock);
private: mutable std::mutex mutex_; std::map<std::string, std::weak_ptr<Stock>> stocks_; };
std::shared_ptr<Stock> StockFactory::get(const std::string& key) { std::lock_guard<std::mutex> lock(mutex_); std::weak_ptr<Stock>& wkStock = stocks_[key]; std::shared_ptr<Stock> pStock = wkStock.lock(); if (pStock == nullptr) { pStock.reset(new Stock(key), [weak_this = std::weak_ptr<StockFactory>(shared_from_this())](Stock* stock) { StockFactory::weakDeleteStock(weak_this, stock); }); wkStock = pStock; } return pStock; }
void StockFactory::weakDeleteStock(const std::weak_ptr<StockFactory>& wkFactory, Stock* stock) { std::shared_ptr<StockFactory> pFactory = wkFactory.lock(); if (pFactory) { pFactory->removeStock(stock); }
delete stock; }
void StockFactory::removeStock(Stock* stock) { if (stock) { std::lock_guard<std::mutex> lock(mutex_); stocks_.erase(stock->key()); } }
|