线程同步之mutex和条件变量

并发编程有两种基本模型,一种是消息传递,另一种是共享内存。在分布式系统中,运行在多台机器上的多个进程的并行编程只有消息传递。在多线程编程中,消息传递更容易保证程序的正确性。在用C/C++编写多线程程序时,我们需要了解共享内存模型下的同步原语。

线程同步的四项原则:

1)首要原则是尽量最低限度地共享对象,减少需要同步的场合。

2)其次是使用高级的并发编程构件,如生产者-消费者队列。

3)最后不得已必须使用底层的同步原语时,只用非递归的互斥器和条件变量,慎用读写锁,不要用信号量。

4)除了使用atomic整数之外,不自己编写lock-free代码,也不要用“内核级”同步原语。

平常看书及看代码,接触到最多的就是底层同步原语了,linux下的同步原语包括互斥器,条件变量,读写锁,和信号量。可能互斥器和条件变量是大家用的最多的了,C++标准库中也提供了互斥器,template<classMutex>class lock_guard,是以模板定义的类,lock_guard 对象通常用于管理某个锁(Lock)对象,因此与 Mutex RAII 相关,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁(注:类似 shared_ptr 等智能指针管理动态分配的内存资源 )。

模板参数 Mutex 代表互斥量类型,例如 std::mutex 类型,是一个基本的 BasicLockable 类型,BasicLockable 类型的对象只需满足两种操作,lock 和 unlock,另外还有 Lockable 类型,在 BasicLockable 类型的基础上新增了 try_lock 操作,因此一个满足 Lockable 的对象应支持三种操作:lock,unlock 和 try_lock。

在 lock_guard 对象构造时,传入的 Mutex 对象(即它所管理的 Mutex 对象)会被当前线程锁住。在lock_guard 对象被析构时,它所管理的 Mutex 对象会自动解锁,由于不需要程序员手动调用 lock 和 unlock 对 Mutex 进行上锁和解锁操作,因此这也是最简单安全的上锁和解锁方式,尤其是在程序抛出异常后先前已被上锁的 Mutex 对象可以正确进行解锁操作,极大地简化了程序员编写与 Mutex 相关的异常处理代码。lock_guard 对象并不负责管理 Mutex 对象的生命周期,lock_guard 对象只是简化了 Mutex 对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个 lock_guard 对象的声明周期内,它所管理的锁对象会一直保持上锁状态;而 lock_guard 的生命周期结束之后,它所管理的锁对象会被解锁。

条件变量是线程可用的另一种同步机制。条件变量给多个线程提供了一个回合的场所。条件变量与互斥器一起使用时,允许线程以无竞争的方式等待特定的条件发生。

条件变量本身是由互斥量保护的。线程在改变条件状态前必须首先锁住互斥量,其他线程在获得互斥量之前不会察觉到这种改变,因为必须锁定互斥量以后才能计算条件。

条件变量的使用方式为,对wait端:

1)必须与mutex一起使用,受mutex保护。

2)在muetx已上锁的时候才能调用wait().

3判断布尔条件和wait()放到while循环中,是为了防止欺骗性唤醒(spurious wakeup)。

实例代码为:

struct msg{
   struct msg* m_next;
};
struct msg* workq;
pthread_cond_t qready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
void process_msg(void){
   struct mad *mp;
   for(;;){
      pthread_mutex_lock(&qlock);
      while(workq == NULL)
         pthread_cond_wait(&qready,&qlock);
      mp = workq;
      workq = mp->m_next;
      pthread_mutex_unlock(&qlock);
   }
}

对于signal/broadcast端:

1)在signal之前一般要修改布尔表达式。

2)修改布尔表达式之前通常用mutex保护。

3)区分signal与broadcast,broadcast通常用于表明状态变化,signal通常用于表示资源可用。

即获取互斥量、改变条件、释放互斥量并向条件变量发送信号。

实例代码为:

void enqueue_msg(struct msg* mp){
   pthread_mutex_lock(&qlock);
   mp->m_next = workq;
   workq = mp;
   pthread_mutex_unlock(&qlock);
   pthread_cond_signal(&qready);
}

互斥器和条件变量构成了多线程编程的全部同步原语,用它们即可完成任何多线程的同步任务,二者不能互相替换。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值