1.1.3 一个线程安全的Counter 示例
编写单个的线程安全的class 不算太难,只需用同步原语保护其内部状态。例如下面这个简单的计数器类Counter:
- 1 // A thread-safe counter
- 2 class Counter : boost::noncopyable
- 3 {
- 4 // copy-ctor and assignment should be private by default for a class.
- 5 public:
- 6 Counter() : value_(0) {}
- 7 int64_t value() const;
- 8 int64_t getAndIncrease();
- 9
- 10 private:
- 11 int64_t value_;
- 12 mutable MutexLock mutex_;
- 13 };
- 14
- 15 int64_t Counter::value() const
- 16 {
- 17 MutexLockGuard lock(mutex_); // lock 的析构会晚于返回对象的构造,
- 18 return value_; // 因此有效地保护了这个共享数据。
- 19 }
- 20
- 21 int64_t Counter::getAndIncrease()
- 22 {
- 23 MutexLockGuard lock(mutex_);
- 24 int64_t ret = value_++;
- 25 return ret;
- 26 }
- 27 // In a real world, atomic operations are preferred.
- 28 // 当然在实际项目中,这个class 用原子操作更合理,这里用锁仅仅为了举例。
这个class 很直白,一看就明白,也容易验证它是线程安全的。每个Counter 对象有自己的mutex_,因此不同对象之间不构成锁争用(lock contention)。即两个线程有可能同时执行L24,前提是它们访问的不是同一个Counter 对象。注意到其mutex_ 成员是mutable 的,意味着const 成员函数如Counter::value() 也能直接使用non-const 的mutex_。思考:如果mutex_ 是static,是否影响正确性和/或性能?
尽管这个Counter 本身毫无疑问是线程安全的,但如果Counter 是动态创建的并通过指针来访问,前面提到的对象销毁的race condition 仍然存在。