虽然循环与空体检查易失性int - 这是什么意思?

Luc*_*uca 34 c++ volatile while-loop

我正在看一个C++类,它有以下几行:

while( x > y );
return x - y;
Run Code Online (Sandbox Code Playgroud)

x并且y是类型的成员变量volatile int.我不明白这个结构.

我在这里找到了代码存根:https://gist.github.com/r-lyeh/cc50bbed16759a99a226.我想这不保证是正确的甚至是工作.

the*_*ker 62

由于x并且y已经被声明为volatile,程序员希望它们将从程序外部进行更改.

在这种情况下,您的代码将保留在循环中

 while(x>y);
Run Code Online (Sandbox Code Playgroud)

并且将在值x-y从外部更改之后返回值x <= y.在您告诉我们有关您的代码的更多信息以及您看到它的位置之后,可以猜到写这个的确切原因.while在这种情况下,循环是一种等待其他事件发生的技术.

  • 但是,如果应用于微控制器,则代码相当精细.芯片可能太小而无法通过定时器中断进行正确的线程或调度.不确定这种方法的使用程度,但这绝对是可能的. (16认同)
  • @Caramiriel:你是对的,情况更糟.由于`x`和`y`是不同的变量,它们会被单独更新和读取,并声明它们`volatile`不会改变它.因此,一个线程可以以一种不打算改变"x> y"结果的方式将两个变量一起改变,但是这个循环可能会读取一个中间状态,其中只有一个变量已被更新,而"x> y"虚假地评估为假.当然,这在调试过程中从未发生过...... (11认同)
  • 也许值得注意的是,如果`x - y`正在被执行,`x> y`可能再次成立(在此期间改变).在进行所有这些检查和计算之前,将它读入局部变量是不是更安全? (5认同)
  • 如果代码的其余部分提供了证明它是安全的额外不变量,那么它也可能没问题.作为一般规则,使用`volatile`而不使用其他同步工具的多线程代码应该被怀疑."volatile"所做的实际规则是非常具体和细微的,但许多开发人员认为它做的事情实际上并没有.(或者更确切地说,它确实在特定情况下在他们的特定编译器上执行它们,所以他们从不学习其他!) (5认同)
  • 请注意,这对于CPU使用率会有不利影响,因为它可能会立即重新迭代几秒钟.老实说,当前线程应至少休眠一毫秒,这已经大大降低了CPU使用率. (4认同)

Sta*_*tas 25

它似乎

while( x > y );
Run Code Online (Sandbox Code Playgroud)

是一个旋转循环.它不会停止直到x <= y.由于xyvolatile,他们可能这个例程之外被改变.所以,一旦x <= y成为现实,x - y将被退回.此技术用于等待某些事件.

更新

根据您添加的要点,似乎想法是实现线程安全的无锁循环缓冲区.是的,实施不正确.例如,原始代码片段是

unsigned count() const {
    while( tail > head );
    return head - tail;
}
Run Code Online (Sandbox Code Playgroud)

即使tail变得小于或等于head,也不能保证head - tail返回正数.调度程序可以在while循环之后立即将执行切换到另一个线程,并且该线程可以改变head值.无论如何,还有许多与读取和写入共享内存工作(内存重新排序等)有关的其他问题,所以请忽略此代码.


Syr*_*ran 20

其他回复已经详细指出了指令的作用,但只是回顾一下,因为y(或head在链接的例子中)被声明为volatile从不同的线程对该变量进行的更改将导致while循环一旦条件已经完成满足.

但是,即使链接代码示例非常短,它也是如何编写代码的近乎完美的示例.

首先是线路

while( tail > head );
Run Code Online (Sandbox Code Playgroud)

将浪费大量的CPU周期,几乎锁定一个核心,直到满足条件.

随着我们的进展,代码变得更好.

buffer[head++ % N] = item;
Run Code Online (Sandbox Code Playgroud)

感谢JAB指出我错误的帖子 - 这里有预增量.纠正了影响. 由于没有locks或mutexes,我们显然必须承担最坏的情况.itemhead++执行之前和之后分配值时,线程将切换.然后,Murphy将再次调用包含此语句的函数,item并在同一head位置指定值.之后head增加.现在我们切换回第一个线程并head再次递增.而不是

buffer[original_value_of_head+1] = item_from_thread1; 
buffer[original_value_of_head+2] = item_from_thread2;
Run Code Online (Sandbox Code Playgroud)

我们最终得到了

buffer[original_value_of_head+1] = item_from_thread2; 
buffer[original_value_of_head+2] = whatever_was_there_previously;
Run Code Online (Sandbox Code Playgroud)

你可能会在客户端使用很少的线程来摆脱这样的草率编码,但在服务器端,这只能被认为是定时炸弹.请使用同步结构,例如locks或mutexes.

好吧,只是为了完整性,这条线

while( tail > head );
Run Code Online (Sandbox Code Playgroud)

在方法中pop_back()应该是

while( tail >= head );
Run Code Online (Sandbox Code Playgroud)

除非你想要再弹出一个元素而不是你实际推入的元素(甚至在推入任何东西之前弹出一个元素).

很抱歉写一些基本归结为长期咆哮,但如果这只让一个人复制和粘贴那些淫秽的代码,那就值得.

更新:我想我也可以举一个例子,其中代码while(x>y);实际上是完全有道理的.实际上你过去经常在"好日子"里看到这样的代码.咳嗽 DOS.虽然没有在线程的上下文中使用.主要是作为一个后备,以防注册中断挂钩是不可能的(你的孩子可能会将其翻译为"无法注册事件处理程序").

startAsynchronousDeviceOperation(..);
Run Code Online (Sandbox Code Playgroud)

这可能是几乎任何事情,例如告诉硬盘通过DMA读取数据,或告诉声卡通过DMA记录,甚至可能调用不同处理器(如GPU)上的功能.通常通过类似的东西发起outb(2).

while(byteswritten==0); // or while (status!=DONE);
Run Code Online (Sandbox Code Playgroud)

如果与设备的唯一通信通道是共享内存,那么就这样吧.不希望现在看到类似于设备驱动程序和微控制器之外的代码.显然假设规范声明内存位置是写入的最后一个.


Oli*_*pro 7

volatile关键字旨在阻止某些优化.在这种情况下,如果没有关键字,编译器可以将while循环展开为具体的指令序列,这些指令显然会在现实中破坏,因为值可以在外部修改.

想象一下:

int i = 2;
while (i-- > 0) printf("%d", i);
Run Code Online (Sandbox Code Playgroud)

大多数编译器会查看这个并简单地生成两个调用printf- 添加volatile关键字将导致它生成CPU指令,调用计数器设置为2并在每次迭代后检查值.

例如,

volatile int i = 2;
this_function_runs_on_another_process_and_modifies_the_value(&i);
while(i-- > 0) printf("%d", i);
Run Code Online (Sandbox Code Playgroud)