(十)线程同步
1. 线程同步的使用原则
- 首要尽量最低限度共享对象,减少需要同步的场合,避免一个对象暴露给别的线程;
- 其次是使用高级的并发编程构件,如线程安全的队列等;
- 最后不得已必须使用底层同步原语来保护共享对象时,只使用非递归的互斥锁和条件变量;
2. 互斥锁
参考: std::mutex
互斥锁单独使用,主要是为了保护共享数据.
std::mutex的使用原则是, 使用RAII手法封装mutex的创建/销毁/加锁/解锁这四个操作, 保证锁的生效期间等于一个作用域, 不会因异常而忘记解锁. RAII能够保证加锁/解锁在同一个线程, 能够保证自动解锁和避免重复解锁.
std::lock_guard
是互斥封装器,为在作用域块期间占有互斥提供便利 RAII 风格机制。
参考std::lock_guard
std::unique_lock
是通用互斥包装器,允许延迟锁定、锁定的有时限尝试、递归锁定、所有权转移和与条件变量一同使用。
参考std::unique_lock
3. 条件变量
条件变量就是一个或多个线程等待某个布尔表达式为真,即等待别的线程"唤醒"它.
条件变量只有一种正确使用的方式:
- 对于wait端:
- 必须与mutex一起使用, 该布尔表达式的读写需受此mutex保护;
- 在mutex已上锁时才能调用wati();
- 把判断布尔条件和wait()放到while循环中. 必须使用while循环来等待条件变量,而不能用if语句,原因是spurious wakeup(虚假唤醒).
- 对于notify端:
- 在notify前一般要修改布尔表达式;
- 修改布尔表达式通常要用mutex保护;
- 区分notify_one和notify_all.