mutex分为递归(以下简写为rm)和非递归(以下简写为nrm)两种,它们的唯一区别在于:同一个线程可以重复对rm加锁,但是不能重复对nrm加锁。
虽然rm使用起来要更加方便一些,并且不用考虑一个线程将自己锁死的问题,但是它可能会隐藏代码中的一些问题。例如:自以为拿到一个锁可以对对象进行修改时,外层代码已经拿到了锁,并且在此时正在修改这个对象。
MutexLock mutex; vector<Foo> foos; void post(const Foo& f) { MutexLockGuard lock(mutex); foos.push_back(f); } void traverse() { MutexLockGuard lock(mutex); for (vector<Foo>::const_iterator it = foos.begin(); it != foos.end(); ++it) { /* 假设Foo::doit()会间接调用post。 */ it->doit(); } }
此时若假设mutex是递归的,将会出现死锁的情况。
若mutex是非递归的,push_back可能会导致vector迭代器失效,从而导致程序崩溃。
由于死锁更容易debug,因此使用nrm可以更快速的找到代码的逻辑错误进行修改。
此时有两种改进方式:一是把对foos的修改推后,先记录要修改的元素,等遍历完foos后再进行修改;二是copy-on-write。