如果不使用volatile,我怎么知道变量是从内存而不是寄存器中读取的.
static uint64 counter;
void thread(void *arg){
while(counter < (uint64)arg){
acquire(&lock)
counter++;
release(&lock);
}
}
我以前从未怀疑过这一点,但如果有编译器优化,它不会产生
如果不使用volatile,我怎么知道变量是从内存而不是寄存器中读取的.
static uint64 counter;
void thread(void *arg){
while(counter < (uint64)arg){
acquire(&lock)
counter++;
release(&lock);
}
}
我以前从未怀疑过这一点,但如果有编译器优化,它不会产生
只要正确实现了锁定库,就不需要volatile
—它(通过设计)将保证正确的行为,尽管编译器优化、线程在同一资源上竞争、在其他处理器和内核上调度等等.
正确实现的acquire
和release
将保证编译器不会产生错误的重新排序,更重要的是,保证processor不会产生错误的重新排序(这可能发生在某些处理器体系 struct 上).
要做到这一点,需要使用屏障.我将其更多地作为对事物如何工作的解释,并将其作为手写此类同步代码的建议:
acquire
函数包括一个带有"acquire semantics"的所谓memory barrier,acquire barrier意味着它后面的任何代码(即关键部分内的代码)都不能被重新排序为发生在barrier之前.这意味着任何其他线程和处理器都必须在从临界段进行任何写入之前看到锁的获取.release
函数提供了一个类似的内存屏障,可防止关键部分中的任何内容在屏障后重新排序.这可以确保其他线程在看到释放锁之前,会看到临界部分的效果.
除非您正在编写一个低级库,并准备彻底证明、验证和测试它,否则您不应该自己编写这种障碍代码.很容易犯细微的错误、性能低效等.