互斥锁保证了线程间的同步,可是却将并行操做变成了串行操做,这对性能有很大的影响,因此咱们要尽量的减少锁定的区域,也就是使用细粒度锁。编程
这一点lock_guard
作的很差,不够灵活,lock_guard
只能保证在析构的时候执行解锁操做,lock_guard
自己并无提供加锁和解锁的接口,可是有些时候会有这种需求。看下面的例子。并发
class LogFile { std::mutex _mu; ofstream f; public: LogFile() { f.open("log.txt"); } ~LogFile() { f.close(); } void shared_print(string msg, int id) { { std::lock_guard<std::mutex> guard(_mu); //do something 1 } //do something 2 { std::lock_guard<std::mutex> guard(_mu); // do something 3 f << msg << id << endl; cout << msg << id << endl; } } };
上面的代码中,一个函数内部有两段代码须要进行保护,这个时候使用lock_guard
就须要建立两个局部对象来管理同一个互斥锁(其实也能够只建立一个,可是锁的力度太大,效率不行),修改方法是使用unique_lock
。它提供了lock()
和unlock()
接口,能记录如今处于上锁仍是没上锁状态,在析构的时候,会根据当前状态来决定是否要进行解锁(lock_guard
就必定会解锁)。上面的代码修改以下:函数
class LogFile { std::mutex _mu; ofstream f; public: LogFile() { f.open("log.txt"); } ~LogFile() { f.close(); } void shared_print(string msg, int id) { std::unique_lock<std::mutex> guard(_mu); //do something 1 guard.unlock(); //临时解锁 //do something 2 guard.lock(); //继续上锁 // do something 3 f << msg << id << endl; cout << msg << id << endl; // 结束时析构guard会临时解锁 // 这句话可要可不要,不写,析构的时候也会自动执行 // guard.ulock(); } };
上面的代码能够看到,在无需加锁的操做时,能够先临时释放锁,而后须要继续保护的时候,能够继续上锁,这样就无需重复的实例化lock_guard
对象,还能减小锁的区域。一样,可使用std::defer_lock
设置初始化的时候不进行默认的上锁操做:性能
void shared_print(string msg, int id) { std::unique_lock<std::mutex> guard(_mu, std::defer_lock); //do something 1 guard.lock(); // do something protected guard.unlock(); //临时解锁 //do something 2 guard.lock(); //继续上锁 // do something 3 f << msg << id << endl; cout << msg << id << endl; // 结束时析构guard会临时解锁 }
这样使用起来就比lock_guard
更加灵活!而后这也是有代价的,由于它内部须要维护锁的状态,因此效率要比lock_guard
低一点,在lock_guard
能解决问题的时候,就是用lock_guard
,反之,使用unique_lock
。学习
后面在学习条件变量的时候,还会有unique_lock
的用武之地。线程
另外,请注意,unique_lock
和lock_guard
都不能复制,lock_guard
不能移动,可是unique_lock
能够!code
// unique_lock 能够移动,不能复制 std::unique_lock<std::mutex> guard1(_mu); std::unique_lock<std::mutex> guard2 = guard1; // error std::unique_lock<std::mutex> guard2 = std::move(guard1); // ok // lock_guard 不能移动,不能复制 std::lock_guard<std::mutex> guard1(_mu); std::lock_guard<std::mutex> guard2 = guard1; // error std::lock_guard<std::mutex> guard2 = std::move(guard1); // error