网上搜下 volatile 的作用是:
"volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错"
“一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在”
“用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份”
有些不好理解,自己理解是:
在代码中有多线程或多进程访问同一个无保护的变量时候,volatile 起到作用是
1,防止编译过程中,编译器对某些代码的误解而错误优化;
2,防止在运行过程中,cpu 对某变量寄存器cache,而导致cache和内存不一致(多线程/进程同时操作某变量)
volatile 在
实际开发中用的很少,在 yifei 代码中,出现在两个地方:
1,快速锁的锁定义
typedef volatile yf_atomic_uint_t yf_atomic_t;
2,无锁环形队列定义
typedef struct yf_task_queue_s
{
yf_u32_t magic;
size_t capacity;
volatile size_t read_offset;
volatile size_t write_offset;
}
yf_task_queue_t;
说下 yifei 项目中 volatile 的两处使用:
1,快速锁
原理其实比较简单,就是利用的 volatile+原子操作;说白了,就是判断某个变量是不是等于0,如果等于0,则将他置为1;否则不停循环
重试;关键需要保证两点:
i),必须保证多个线程/进程读到的变量是同一个地方的,所以不能有寄存器缓存,所以需要 volatile;
ii),光有 volatile 还是不行的,因为 volatile 只是保证了读取的地方是一致的,但并不能保证读然后写这一系列操作的原子性,他也保证不
了,所以怎么实现读+写原子性,需要其他手段,比如代码中各种条件下各种不同的手段:atomic_cmpxchg,__sync_bool_compare_and_swap
btw: 多线程/进程下 volatile 是有用的,网上看到有些人把 volatile 和加锁混淆在一起,顺便总结下:
i),
如果不加锁,则必须有 volatile ,但光用 volatile 是不够的,必须保证操作的原子性才能避免锁;
ii),
如果加锁(比如用pthread_mutex),则 volatile 是不需要的,加了反而影响效率(寄存器cache被禁了)
ii),锁本身这一内存数据肯定是 volatile,不管这锁是怎么实现的;
2,无锁环形队列
生产者消费者模型通常需要一个队列做中介,如果是多线程或多进程实现,是需要加锁的;锁本身有开销,且锁有一定的风险;
比如:多进程下,如果某进程把持了锁,在未解锁前异常退出,会导致队列锁死;
在一对一的特殊情况下,其实是可以充分利用环形队列的特点,实现无锁;
关键点是这两个 volatile ,这里的操作不是原子性的,但因为这一数据结构的特点加使用者的特点(一对一),所以才能保证不需要原子性也
能避免锁的开销(呃,没说清楚,实现确实比较巧妙,有点”只可意会不可言传“的味道);
yifei -- 高效可移植 linux c/c++ server 开发基础库
阅读(1409) | 评论(0) | 转发(0) |