ILD

CAS (compare and swap)
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2026-6-10 站点:Inside Linux Development

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




Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.