锁的实现方法是解决多核CPU系统性能问题的关键。
1. 回顾下众所周知的两种机制: test & set, compare & swap.
test & set:
enter_region:
tsl reg, flag;
cmp reg, #0;
jnz enter_region;
不停读入多核共享内存中的flag到寄存器, (同时)把内存中的flag设置为1
然后看寄存器中的值是否为0, 如果是0,那么拿到锁。
其中同时是由tsl的原子性保证的。说简单点,一般的指令完成一个动作,这个指令完成动作有两个,一是把flag从内存中搞到寄存器中,二是把内存中的flag设为1. 由于中断不能中断一条指令, 这个指令相当与:
Disable interrupt;
reg = *flag;
*flag = 1;
Enable interrupt
compare & swap:
int CAS(ulong *mem, ulong newval, ulong oldval)
{
__typeof (*mem) ret;
__asm__ volatile (
"lock;
cmpxchg %2, %1
:"=a(ret)", "=m"(*mem)
:"r"(newval), "m"(*mem), "o"(oldval)");
}
这里有牛xx的AT&T汇编,稍微解释下
cmpxchg的意思是比较他的第一个参数和eax寄存器中的值, 如果相等,那么把第一个参数换成第二个参数的值.
这里的第一个参数是%2, 第二个参数是%1
%2表示, 从输出部(第一个冒号开始)开始的第二个参数(老规矩, 0开始)就是"r"(newval)代表的变量。
"r"表示newval告诉gcc要用寄存器装。
%1表示, 第一个参数就是"=m(*mem)"
=m表示参数在内存中,参数位置是mem
a(ret)表示ret装入eax, 从typedef知道,其实就是把mem中的内容装入eax
综合上面的描述,CAS的逻辑:
比较内存中一变量(mem)的值和你以为他应该拥有的值,如果相等,说明别人改过,需要用这个API重新测试过。
这两种方式的区别:
test&set不管三七二十一,更新内存中flag,CAS不会。
当然CAS有ABA问题,实际可以应该同时比较newval&oldval
2. 问题描述
假设是线程工作组模式,即一组线程独立做类似,但根据输入不全相同的事情
pthread_func()
{
before lock process
thread_lock()
do_something;
thread_unlock()
after lock process
}
假设A,B线程的实例同时开始处理事件,那么,A处理完后,到thread_lock将会被锁定。B就不能满负荷运作。
3. 解决方案
本着提出问题的同学可以不用解决问题的原则,有兴趣的同学请给个思路:一个显然的解决方法是, 优化设计,使这些线程处理速度类似。
简单说-fopenmp不算,当然如果有高手能讲清楚openMP的机制,请指点下,多谢。