我有一个线程,它基本上做到了:
int changed; //global variable
..
for (;;) {
pthread_mutex_lock(&mtx);
if (changed) {
do_changes();
changed = 0;
}
pthread_mutex_unlock(&mtx);
do_stuff();
}循环每秒运行几十万次,而全局changed变量很少(一天几次)由另一个线程设置。
通过更改为
volatile int changed; //global variable
..
for (;;) {
if (changed) {
pthread_mutex_lock(&mtx);
do_changes();
changed = 0;
pthread_mutex_unlock(&mtx);
}
do_stuff();
}使用这种方法,我可以测量到循环的性能提高了3-4%,这是值得追求的。
然而,不鼓励使用易失性变量。这里的方法有什么缺点吗?有没有可能导致2.版本不能按预期工作的情况?
发布于 2014-08-29 18:17:02
volatile不会使您的变量线程安全或原子。为此,您可能会喜欢使用C11 atomics。
基本上,您有两个线程更改changed变量,从而覆盖先前的值,从而导致数据争用。
我再怎么推荐你看atomic Weapons: The C++ Memory Model and Modern Hardware也不为过。(它也适用于C语言)。
发布于 2014-08-29 18:53:12
作者:但是,似乎非常不鼓励使用易失性变量。这里的方法有什么缺点吗?有没有可能导致2.版本不能正常工作的情况?
首先:每当你想要使用volatile .时,请三思,如果你现在不需要,看看会发生什么:
1)如果循环在多个线程中,这是不安全的。你可以考虑复制支票:
if (changed) { // quick check
pthread_mutex_lock(&mtx);
if (changed) { // another thread could do the work
...2)如果您的代码对查看更改很关键,则需要使用原子,因为pthread_mutex_lock之前的if(changed)可能因为缓存而看不到它。
3)它可以在具有强内存排序和原子int访问的x86(_64)上工作,但在其他体系结构上失败。这就是为什么不鼓励使用volatile的原因,使用原子(并习惯于使用它)。volatile 不强制原子使用或任何其他同步。原子做(读-修改-写指令)。
std::atomic_flag validated;
std::mutex mx; struct MyData { ... } data;
void change() {
lock_guard<mutex> lock(mx);
data.something();
validated.clear();
}
void validate() {
if(!validated.test_and_set()) {
lock_guard<mutex> lock(mx);
data.update();
}
}注意::除非您持有锁并为其使用另一个变量,否则您永远无法确定数据是否有效。
4)只需使用pthread_spinlock_t试用您的原始代码
5)小建议:除非你真的知道你在做什么,否则不要扮演同步的上帝。您可以从mutex切换到spinlock (由其他人编写)并进行一些基准测试。
关于编辑和评论的:最初的答案是从1)什么都没有开始。事实证明,有些人既没有阅读完整的问题,也没有阅读完整的答案。皮蒂,那些快速倒下的选民。这个网站不是facebook,这些投票应该是是有帮助的或是没有帮助的,这些向上/向下的投票在facebook上没有喜欢和不喜欢!我可能不同意其他答案的某些部分,但仍然认为它们是有帮助的,虽然不完整(没有回答完整的问题,但只是其中的一部分)或部分不一致(如果我们知道可以没有问题地将相同的值从多个线程写入变量,则没有什么不好的)。
发布于 2014-08-29 19:03:39
如果您使用的是支持它们的C11环境,则可以使用atomic variables。如果你的系统支持它,他们使用特殊的指令来实现原子性,而不是锁。如果你的系统不支持这一点,他们使用锁(标志类型总是无锁的)。
如果您没有C11,但是有一个与GCC兼容的编译器,请参阅sync函数族。它与C11原子变量类似(但更老),但是如果您的系统不支持它们,它们会生成一个函数调用。
https://stackoverflow.com/questions/25566016
复制相似问题