线程同步四原则
1.减少对象暴露给别的线程
2.使用高级的并发编程组件
3.必须使用同步原语时,只用非递归的互斥器和条件变量,不用读写锁和信号量
4.lock-free仅限于atomic
互斥器
用RAII手法包装互斥器,只用非递归的mutex,不手工调用lock和unlock
条件变量
条件变量只有如下这一种推荐的使用方法,几乎不出错
LockGraund lk(m_mux);
while(m_taskQueue.empty() && m_running){
m_condEmpty.wait(lk);
}
在等待端:为什么只能用while而不能用if,原因:一个消费者线程阻塞,另一个消费者线程正在运行,而且刚通过检查,此时A被唤醒,两个线程同时消费一个数据,产生错误(spurious wakeup)
在唤醒端:修改bool表达式需要用mutex保护,区分notify_one和notify_all(前者用于表示资源可用,后者用于表示状态发生变化)
互斥器和条件变量组成了多线程编程的全部原语,用这两个手段可以完成所有的多线程编程
线程安全的单列实现
template< typename T>
class Singleton{
public:
T &getInstance(){
std::call_once(once,&Singleton::initSingleton);
return *t;
}
T *getInstance(){
std::call_once(once,&Singleton::initSingleton);
return t;
}
static void initSingleton(){
t = new T();
}
private:
static std::once_flag once;
static T* t;
};
template<typename T>
std::once_flag Singleton<T>::once;
template<typename T>
T *Singleton<T>::t = NULL;
利用shared_ptr实现copy on write
class SharedPtrCopyOnWrite{
public:
SharedPtrCopyOnWrite(){
someDataTobeShared = std::make_shared<vector<int>> (2,3);
}
void reader(){
std::shared_ptr<vector<int>> localPtr;
{
LockGraund lk(m_mux);
localPtr = someDataTobeShared;
}
///some read op
for(auto i:*localPtr){
}
}
void write(){
LockGraund lk(m_mux);
if(!someDataTobeShared.unique()){
std::shared_ptr<vector<int>> newData(new vector<int>(*someDataTobeShared));
someDataTobeShared.swap(newData);
}
assert(someDataTobeShared.unique());
///some write op
(*someDataTobeShared)[0] = 3;
}
private:
std::shared_ptr<vector<int>> someDataTobeShared;
std::mutex m_mux;
};