线程同步2019-10-31
class Request
{
public:
void process() // __attribute__ ((noinline))//不让函数内敛
{
muduo::MutexLockGuard lock(mutex_);
print(); //添加它很容易产生死锁,在本程序中。
}
void print() const // __attribute__ ((noinline))
{
muduo::MutexLockGuard lock(mutex_);
}
private:
mutable muduo::MutexLock mutex_;
};
int main()
{
Request req;
req.process();
}
gdb中bt列出调用栈
mutex是加锁原语。条件变量的学名叫管程,是用来一个或多个线程等待某个表达式为真,即等待别的线程“唤醒”它。
条件变量只有一种正确的使用方式,几乎不可能用错。对于wait端:
1、必须与mutex一起使用,该布尔表达式的读写需受此mutex保护。
2、在mutex已上锁的时候才可以调用wait()。
3、把判断布尔条件和wait()放到while循环中。
对于signal/broadcast端:
1、不一定要在mutex已上锁的情况下调用signal(理论上)。
2、在signal之前一般要修改布尔表达式。
3、修改布尔表达式通常要用mutex保护。
4、注意区分signal与broadcast:“broadcast通常用于表明状态变化,signal通常用于表示资源可用。”
倒计时:主要的用途:
1、主线程发起多个子线程,等这些子线程各自都完成一定任务之后,主线程才继续执行。通常用于主线程等待多个子线程完成初始化。
2、主线程发起多个子线程,子线程都等待主线程,主线程完成其他一些任务之后通知所有子线程开始执行。通常用于多个子线程等待主线程发出“起跑"命令。
互斥器(mutex)和条件变量构成了多线程编程的全部必备原语,用他们即可完成任何多线程的同步任务,二者不能相互替代。
读写锁(rwlock)。
不要用读写锁和信号量。
muduo库不提供读写锁的封装。
MutexLock MutexLockGuard Condition 介绍
//MutexLock类
class MutexLock : boost :: noncopyable
{
public:
MutexLock():holder_(0)
{
pthread_mutex_init(&mutex_,NULL);
}
~MutexLock()
{
assert(holder_ == 0);
pthread_mutex_destroy(&mutex);
}
bool isLockedByThisThread()
{
return holder_ == CurrentThread::tid();
}
void assertLocked()
{
assert(isLockedByThisThread());
}
void lock() //仅供MutexLockGuard调用,严禁用户代码调用
{
pthread_mutex_lock(&mutex_); //这两行顺序不能反
holder_ = CurrentThrea::tid();
}
void unlock() //仅供MutexLockGuard调用,严禁用户代码调用
{
holder_ = 0; //这两行顺序不能反
pthread_mutex_unlock(&mutex_);
}
pthread_mutex_t* getPthreadMutex()//仅供Condition调用,严禁用户代码调用
{
return &mutex_;
}
private:
pthread_mutex_t mutex_;
pid_t holder_;
};
//MutexLockGuard类
class MutexLockGuard : boost::noncopyable
{
public:
explicit MutexLocakGuard(MutexLock& mutex) : mutex_(mutex)
{
mutex_.lock();
}
~MutexLockGuard()
{
mutex_.unlock();
}
private:
MutexLock& mutex_;
};
#define MutexLockGuard(x) static_assert(false,"missing mutex guard var name")
这个宏的作用是防止程序里出现如下错误:
void doit()
{
MutexLockGuard(mutex); //遗漏变量名,产生一个临时对象又马上销毁了。
//结果没有锁住临界区
//正确写法MutexLockGuard Lock(mutex);
//临界区
}
//Condition类
class Condition : boost::noncopyable
{
public:
explicit Condition(MutexLock& mutex) : mutex_(mutex)
{
pthread_cond_init(&pcond_,NULL);
}
~Condition()
{
pthread_cond_destroy(&pcond_);
}
void wait()
{
pthread_cond_wait(&pcond_,mutex_.getPthreadMutex());
}
void notify()
{
pthread_cond_signal(&pcond_);
}
void notifyAll()
{
pthread_cond_broadcast(&pcond_);
}
private:
MutexLock& mutex_;
pthread_cond_t pcond_;
};
如果一个class要包含MutexLock和Condition,注意:mutex_应先于condition_构造,并作为后者的构造参数。
class CountDownLatch
{
public:
CountDownLatch(int count) : mutex_(),condition_(mutex_),count_(count) //初始化顺序要与成员声明保持一致
{
}
private:
mutable MutexLock mutex_; //顺序很重要,先mutex后condition
Condition condition_;
int count_;
};
//mutex和condition variable 是非常底层的原语,主要用来实现更高级的并发编程工具。
//线程安全的Singleton实现
template<typename T>
class Singleton : boost::noncopyable
{
public:
static T& instance()
{
pthread_once(&ponce_,&Singleton::init);
return *value_;
}
private:
Singleton();
~Singleton();
static void init()
{
value_ = new T();
}
private:
static pthrea_once_t pconce_;
static T* value_;
};
//必须在头文件定义static变量
template<typename T>
pthread_once_t Singleton<T>::ponce_ = PTHREAD_ONCE_INIT;
template<typename T>
T* Singleton<T>::value_ = NULL;
线程等待分为两种:一种是等待资源可用,一种等待进入临界区。
总结:
1、线程同步四原则,尽量用高层同步设施(线程池、队列、倒计时);
2、使用普通互斥器和条件变量完成剩余的同步任务,采用RAII惯用手法(idiom)和Scoped Locking。