互斥锁的function是否足够没有易失性?
我和同事一起编写运行在x86,x64,Itanium,PowerPC和其他10年历史的服务器CPU上的各种平台的软件。
我们只讨论了pthread_mutex_lock(),pthread_mutex_unlock()等互斥函数是否足够,或者被保护的variables是否需要变化。
int foo::bar() { //... //code which may or may not access _protected. pthread_mutex_lock(m); int ret = _protected; pthread_mutex_unlock(m); return ret; }
我关心的是caching。 编译器是否可以在堆栈或寄存器中放置_protected的副本,并在赋值中使用陈旧的值? 如果不是,什么阻止了这种情况发生? 这种模式的变化是脆弱的吗?
我认为编译器实际上并不知道pthread_mutex_lock()是一个特殊的函数,所以我们只是通过序列点保护?
非常感谢。
更新:好的,我可以看到一个趋势与解释为什么波动是不好的。 我尊重这些答案,但关于这个问题的文章很容易在网上find。 我在网上找不到的东西,也是我问这个问题的原因,就是我没有变化的保护。 如果上面的代码是正确的, 那么对于caching问题怎么办?
如果上面的代码是正确的,那么对于caching问题怎么办?
直到C ++ 0x,它不是。 而且它没有在C中指定。所以,它确实取决于编译器。 一般来说,如果编译器不能保证它将遵守对涉及多个线程的函数或操作的内存访问的sorting约束,那么将无法使用该编译器编写multithreading安全代码。 见Hans J Boehm的主题不能作为一个图书馆来实现 。
至于什么抽象你的编译器应该支持线程安全的代码, 内存壁垒维基百科条目是一个很好的起点。
(至于为什么人们提出volatile
,一些编译器把volatile
视为编译器的内存屏障,这绝对不是标准。)
最简单的答案是multithreading不需要volatile
。
长时间的答案是像关键部分那样的顺序点是依赖于平台的,就像你使用的任何线程解决scheme一样,所以大部分的线程安全也是平台依赖的。
C ++ 0x有一个线程和线程安全的概念,但是当前的标准没有,因此volatile
有时会被错误地识别为阻止multithreading编程的操作和内存访问的重新sorting,因为它从来没有打算和不可靠用这种方式。
在C ++中唯一使用volatile
是允许访问内存映射设备,允许在setjmp
和longjmp
之间使用variables,并允许在信号处理程序中使用sig_atomic_t
variables。 关键字本身不会使可变的primefaces。
好消息在C ++ 0x我们将有STL构造std::atomic
可以用来保证primefaces操作和线程安全结构的variables。 直到你select的编译器支持它,你可能需要转向boost库或者破坏一些汇编代码来创build你自己的对象来提供primefacesvariables。
PS很多混淆是由Java和.NET引起的,实际上用关键字volatile
C ++强制执行multithreading语义,但在C不适用的情况下却适用于C语言。
您的线程库应该包含互斥锁和解锁的适当的CPU和编译器屏障。 对于GCC,asm语句中的memory
clobber充当编译器障碍。
实际上,有两件事情可以保护你的代码(编译器)caching:
- 您正在调用非纯外部函数(
pthread_mutex_*()
),这意味着编译器不知道该函数不会修改您的全局variables,因此必须重新加载它们。 - 正如我所说,
pthread_mutex_*()
包含一个编译器障碍,例如:在glibc / x86上,pthread_mutex_lock()
最终调用具有memory
clobber的macroslll_lock()
,强制编译器重新加载variables。
volatile关键字是编译器提示variables可能会在程序逻辑之外改变的一个提示,例如可能作为中断服务程序的一部分而改变的内存映射硬件寄存器。 这可以防止编译器假定caching的值始终是正确的,并且通常会强制读取内存以检索值。 这种用法在线程前几十年左右。 我已经看到它与由信号操纵的variables一起使用,但我不确定使用是否正确。
由不同的线程读取或写入时,由互斥锁保护的variables保证是正确的。 线程API需要确保这些variables视图是一致的。 这个访问是你的程序逻辑的一部分,volatile关键字在这里是不相关的。