如果一个mutex
被许多模块和类共享,就使用boost::recursive_mutex
,如果只被一个模块和类使用,而且不需要re-entrant特性, 就使用boost::mutex
特点:
- 递归锁是互斥锁的一种,同一线程对其多次加锁不会产生死锁。
- 递归锁会使用引用计数机制,以便可以从同一线程多次加锁、解锁,当加锁、解锁次数相等时,锁才可以被其他线程获取。
- 能用普通互斥锁就不要用递归锁,会增大开销
- 无法定义lock递归锁的次数,当然也不是无限的,次数太多会有报错
- 递归锁也可以在循环中使用
1 |
|
这种情况下,线程正常运行,不会死锁。
但是这种情况下,线程就死锁了,只能运行foo
,因为mutex不可重入,foo
这个线程没有运行完1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using namespace std;
boost::mutex mtx;
void bar()
{
boost::mutex::scoped_lock lLock(mtx); // dead-locked here
std::cout << "bar" << std::endl;
}
void foo()
{
boost::mutex::scoped_lock lLock(mtx);
std::cout << "foo" << std::endl;
bar();
}
int main(int argc, char **argv)
{
ros::init(argc, argv, "test_curve");
ros::NodeHandle n;
foo();
return 0;
}
如果把mutex
换成recursive_mutex
,又能正常运行了。
recursive_mutex类可多次进入锁,这样在递归时可以避免一次死锁的几率,这是原本BOOST中所体现的一种思想。递归锁可多次锁,同样的需要多次解锁,那么问题就来了,如果真正或可能造成这次死锁的位置在某个已经进入死锁的地方,那么死锁的位置可能并不是你需要的位置,简单的说,如果A锁了3次,B锁一次,C锁一次,这样就锁出现死锁,排错之困难可想而知。
1 |
|
thread_1
里如果lock
3次,unlock
1次,运行时会阻塞在thread_1
。只有3次unlock
才会正常运行两个线程。