CAS是计算机科学中的一个重要概念,用来实现锁机制和原子操作等。
先来看一个例子:
long v = 0; long count = 1000000; | |
void thread1() { for (int i = 0; i < count; i++) v++; } | void thread2() { for (int i = 0; i < count; i++) v++; } |
两个线程同时对同一个变量执行自增操作100万次。结果并不是200万。
因为自增拆解为2步。
1 int a = v;
2 v = a + 1;
如果线程1跑到1,线程二也跑到1,然后再分别跑到2,那么一个会覆盖另外一个,导致其中一次自增失效。
gcc提供了CAS的builtin函数。
bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...)
如果ptr内存中的值和oldval相同,那么就把newval赋值给它,返回true。
如果不同,则不赋值,并且false。
上述操作保证指令级的原子操作。
用来实现整数原子自增。
for (;;) {
int old = v0;
if (__sync_bool_compare_and_swap(&v0, old, old + 1))
break;
}如果v0的值不是原值,那么自增会失败,会使用新的v值重试。
用来实现锁:
void spin_lock(int *lock) {
while (!__sync_bool_compare_and_swap(lock, 0, 1));
}
void spin_unlock(int *lock) {
*lock = 0;
}很简单,0表示没有锁,1表示有锁。
如果是0,则赋值1,表示上锁,否则一直重试,直到拿到锁。
释放锁很简单,直接赋值0即可。
一个例子可以证明:
#include <pthread.h>
#include <stdio.h>
int v0 = 0, v1 = 0, v2 = 0;
int count = 100*10000;
int lock0 = 0;
void spin_lock(int *lock) {
while (!__sync_bool_compare_and_swap(lock, 0, 1));
}
void spin_unlock(int *lock) {
*lock = 0;
}
void *fn(void *)
{
int i;
for (i = 0; i < count; i++) {
for (;;) {
int old = v0;
if (__sync_bool_compare_and_swap(&v0, old, old + 1))
break;
}
spin_lock(&lock0);
v1++;
spin_unlock(&lock0);
v2++;
}
}
void main()
{
pthread_t a[2];
int i;
for (i = 0; i < 2; i++)
pthread_create(&a[i], NULL, fn, NULL);
for (i = 0; i < 2; i++)
pthread_join(a[i], NULL);
printf("v0/1/2 %d/%d/%d, excpect %d\n", v0, v1, v2, count * 2);
}运行结果:
$ ./a.out
v0/1/2 2000000/2000000/1982863, excpect 2000000
没有任何保护的v2,它的运行结果不是200万。
参考:
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html