关于挥发性使用的规则是严格的吗?

时间:2010-11-12 20:20:59

标签: c++ multithreading rules volatile

我看过这句话:

  

一般规则是,如果你有   必须的原始类型的变量   在多个线程之间共享,   声明那些变量   易失性

来自this article,这句话:

  

一般情况下,可能是任何数据   应该是日期不规则的   宣称是不稳定的。

来自this page

,现在考虑这个引入的规则,我想知道你能举出一个案例的例子,尽管存在对数据的异步访问,声明数据volatile在实践中没用,或者没有这种例外情况和规则是严格的。

8 个答案:

答案 0 :(得分:12)

我记得当那篇文章发表时,我记得在comp.lang.c ++上主持的无休止的讨论。

IIRC, Andrei劫持了volatile关键字 ,用它来区分不同的函数重载。 (请参阅 this article by Scott Meyers 了解另一个这样的想法。)他所做的很棒,因为 它允许编译器抓住你你搞砸了 对对象的受保护和不受保护的访问(非常类似于编译器捕获你应该尝试修改常量)。但除了它可以帮助您之外, 与实际保护对象的并发访问无关

问题是只有90%的人只看了一眼文章而 他们看到的只是同一文章中的volatile和“主题” 。根据他们的知识,他们要么 得出错误的结论,volatile对线程有好处 (你似乎已经这样做了)或者他们对他大喊大叫导致其他人得出错误的结论。
很少有人能够真正完整地阅读这篇文章,并了解他的真实所在。

答案 1 :(得分:11)

我不能代表实际的异步访问场景,因为我不太擅长多线程,但volatile修饰符的作用是告诉编译器:

“听着,这个可能随时改变,所以不要将它缓存或放入寄存器或做任何类似的疯狂,好吗?”

它不能防止异步写入,它只是禁用在外部力量可以更改变量时无效的优化。

编辑: 作为一个潜在的例子,一个不涉及多线程的例子(但是,确实涉及异常复杂的代码;),这里有一个volatile很重要的情况:

volatile bool keepRunning = true;
void Stuff() {
    int notAPointer = 0;

    notAPointer = (int)(&keepRunning); //Don't do this! Especially on 64-bit processors!

    while(keepRunning) {
        *(bool*)(notAPointer) = false;
    }

    printf("The loop terminated!");
}

如果没有那个volatile修饰符,编译器可能会“嘿,keepRunning永远不会被修改,所以我甚至不需要生成检查它的代码!”,实际上我们只是秘密修改它。

(实际上,这可能仍然适用于非优化构建。而且,如果编译器是智能的并且注意到指针被捕,它也可能仍然有用。但是,原理是相同的)

答案 2 :(得分:3)

阅读this。易失性与多线程无关。

答案 3 :(得分:2)

为了跟进Mike的回答,它在这样的情况下很有用(全局变量用于避免此示例的复杂性):

static volatile bool thread_running = true;

static void thread_body() {
    while (thread_running) {
        // ...
    }
}

static void abort_thread() {
    thread_running = false;
}

根据复杂thread_body的不同,编译器可能会选择在线程开始运行时将thread_running的值缓存到寄存器中,这意味着它将永远不会注意到值是否变为false。 volatile强制编译器发出代码,检查每个循环上的实际thread_running变量。

答案 4 :(得分:2)

我会提出一个相当严格但非常有用的规则:如果你不完全理解volatile的作用,请不要使用它。相反,请使用lock。如果您不确切了解lock的作用以及如何使用它,请不要使用多线程。

答案 5 :(得分:2)

我会说理论上这些规则是绝对正确的,每当2个线程访问变量时都需要volatile。 (即使使用互斥锁,也不会阻止编译器优化。)但实际上,编译器在识别可能在特定函数之外修改变量的情况时已足够好,因此其值不应缓存在寄存器中。在大多数情况下,不需要挥发性物质。

我曾经在MSVC中通过检查不同情况下的汇编程序输出进行了一些测试,并且防止变量被缓存所需要的只是让另一个函数写入同一个变量或获取其地址。除非打开“整个程序优化”,否则全局变量永远不会被优化(因此编译器可以确保变量不会在其他翻译单元中被修改)。

答案 6 :(得分:0)

同样,C ++标准没有规定volatile应该如何工作,你必须看一下特定编译器对特定平台的作用。 volatile也有点微妙,它的作用取决于编译器和目标平台的内存模型。锁的使用方式更直观。

答案 7 :(得分:0)

在您首先认真对待与之相关的文章之前,您可能需要阅读another。在后来的posting on Usenet中,安德烈后来承认原始文章的某些部分是完全错误的,并指出这一部分是对事物的更真实的看法(尽管注意到他给它的链接似乎有过时了。