线程同步2019-10-31

线程同步2019-10-31
图片发自简书App


线程同步2019-10-31
图片发自简书App

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列出调用栈


线程同步2019-10-31
图片发自简书App

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);

  //临界区

}


线程同步2019-10-31
图片发自简书App

//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。