多线程lock心得
Cpu硬件已经进入多核时代。多线程问题越来越难以回避,但是多线程是开发人员永远的噩梦。如何减轻多线程开发的痛苦,我总结下自己的一些经验心得,以求抛砖引玉,欢迎大家多提意见,互相学习进步。一、 多线程开发准则:
1. 不要用多线程。
2. 非用不可时,明白其成本,极其昂贵。
3. 尽量少用多线程共享变量,多用栈内变量(小对象)和TLS(大对象)
4. 用好的 设计模式 去解决问题,而不是寄希望于梳理流程。
二、 锁的粒度(granularity)
1. 粗粒度。优点是安全,缺点是可能限制并发度,性能较差。鼓励使用。
2. 细粒度。优点是性能好,但是安全上有隐患。可以把细粒度看为一种性能优化。优化原则是:除非绝对必要,不要做优化。因此,不鼓励使用。
安全隐患在于:
1) 因为粒度较细,某些空隙可能会丢锁(该锁而未锁)。这个问题无解,是由多线程问题的复杂性本质决定的。
2) 较细的粒度,需要更多锁,这就增大了死锁的可能性。同样的原因,死锁问题也无解。
三、 安全性问题
1. 丢锁。没什么好说的,有可能多线程共享的资源就配一把锁。
2. 死锁。参考原则4,用好的 设计模式 去解决问题,而不是寄希望于梳理流程。
四、 一些比较好的设计模式
1. Thread-Safe Interface
这个设计模式的特点是,以较小的成本,避免递归死锁。避免递归死锁常用recursive mutex,但是这种方法,每次调用都会上一次锁,成本较高。而Thread-Safe Interface只会锁一次。
具体思路:接口方法加锁,实现方法不加锁。如:
class A {
public:
bool find (int i) {
Guard<LOCK> guard (lock_);
return find_i(i);
}
void insert(int i) {
Guard<LOCK> guard (lock_);
insert_i(i);
}
private:
bool find_i(int i) {
// search implementation here
}
void insert_i(int i) {
if find_i(i)// must not be find(i)
return;
// insertion implementation here
}
};
例中,接口方法只负责加锁,实现方法具体做事情。实现方法不可以再call interface method,只能call 相应的implementation method。这样锁会且只会被上一次,既安全又高效。
这个模式的正式定义和解释在POSA2(《Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects, Volume 2》),ACE中有很多地方用了此模式。
[ 本帖最后由 wishel 于 2010-1-21 18:18 编辑 ] 2. 保持锁的顺序。
将可能同时申请的锁排序,每个锁分配一个序号,申请多个锁时,应该按顺序申请。这样可以避免交叉死锁。
这一模式可以配合Thread-Safe Interface一起使用。例如:
class A2;
class A1 {
public:
void f(A2& a2) {
Guard<LOCK> guard (lock_);
f_i(a2);
}
private:
void f_i(A2& a2) {
// ……
a2.g(); // right! a1's impl function call a2’s interface function
}
};
class A2 {
public:
void g() {
Guard<LOCK> guard (lock_);
g_i();
}
private:
void g_i() {
// ……
}
};
class A3 {
public:
void h(A1& a1,A2& a2) {
Guard<LOCK> guard (lock_);
h_i(a1,a2);
}
private:
void h_i(A1& a1,A2& a2) {
// ……
a1.f(a2); // wrong! a3's impl function call a1’s interface function
}
};
总结:这个方式,简单清晰,而又不失效率。表面看起来似乎对设计有所限制,a1可以call a3,但a3不可以call a1。但是这是为了保证正确的锁顺序,如果a3确实有call a1的需要,即a1 < a3 && a3 < a1,则a1 == a3,可把a1和a3的共享资源合并,划归一个类管理。 为什么呢?
可否介绍一下,你现在的项目产品都是1个线程在跑吗? 不能说“都”,但是多数是1线程的。
页:
[1]