线程安全的 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()); } }
|