While 循环检查 volatile int 的空体 - 这是什么意思?
While loop with empty body checking volatile ints - what does this mean?
我正在查看 C++ class,其中包含以下几行:
while( x > y );
return x - y;
x
和y
是volatile int
类型的成员变量。我不明白这个结构。
我在这里找到了代码存根:https://gist.github.com/r-lyeh/cc50bbed16759a99a226。我想它不能保证是正确的甚至是有效的。
由于 x
和 y
已被声明为可变的,程序员期望它们将从程序外部更改。
在这种情况下,您的代码将保留在循环中
while(x>y);
并将 return 值从外部更改为 x-y
后 x <= y
。在您告诉我们更多关于您的代码以及您在哪里看到它之后,就可以猜到为什么要这样写的确切原因。本例中的 while
循环是一种等待其他事件发生的技术。
好像
while( x > y );
是一个spinning loop。直到 x <= y
才会停止。由于 x
和 y
是 volatile
,因此可以在此例程之外更改它们。因此,一旦 x <= y
变为真,将返回 x - y
。此技术用于等待某些事件。
更新
根据您补充的the gist,这个想法似乎是实现线程安全的无锁循环缓冲区。是的,实施不正确。例如,原始代码片段是
unsigned count() const {
while( tail > head );
return head - tail;
}
即使tail
变得小于等于head
,也不保证head - tail
returns是正数。调度程序可能会在 while
循环之后立即将执行切换到另一个线程,并且该线程可能会更改 head
值。无论如何,还有很多其他问题与读取和写入共享内存的工作方式(内存重新排序等)有关,所以请忽略这段代码。
volatile
关键字旨在防止某些优化。在这种情况下,如果没有关键字,编译器可以将您的 while
循环展开为具体的指令序列,这显然会破坏现实,因为可以在外部修改这些值。
想象一下:
int i = 2;
while (i-- > 0) printf("%d", i);
大多数编译器会查看这个并简单地生成对 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);
其他回复已经详细指出了指令的作用,但只是回顾一下,因为 y
(或链接示例中的 head
)被声明为 volatile
从不同的线程对该变量所做的更改将导致 while
循环在满足条件后完成。
然而,尽管 linked code example 很短,但它是一个近乎完美的例子,说明了如何 不 编写代码。
首先是行
while( tail > head );
会浪费大量的 CPU 周期,在满足条件之前几乎锁定一个核心。
随着我们的进步,代码会变得更好。
buffer[head++ % N] = item;
感谢 JAB 指出我在这里把 post- 误认为是 pre-increment。更正了含义。
由于没有 lock
s 或 mutex
es,我们显然必须假设最坏的情况。线程将在 item
中赋值之后和 head++
执行之前切换。然后 Murphy 将再次调用包含此语句的函数,在相同的 head
位置分配 item
的值。
之后 head
递增。现在我们切换回第一个线程并再次递增 head
。所以而不是
buffer[original_value_of_head+1] = item_from_thread1;
buffer[original_value_of_head+2] = item_from_thread2;
我们最终得到
buffer[original_value_of_head+1] = item_from_thread2;
buffer[original_value_of_head+2] = whatever_was_there_previously;
你可能会在客户端用很少的线程编写这样草率的代码,但在服务器端这只能被认为是一个滴答作响的定时炸弹。请改用 lock
s 或 mutex
es 等同步结构。
好吧,为了完整起见,行
while( tail > head );
在方法中 pop_back()
应该是
while( tail >= head );
除非您希望能够比实际推入的元素多弹出一个元素(或者甚至在推入任何内容之前弹出一个元素)。
很抱歉写下基本上可以归结为长篇大论的内容,但如果这只能阻止一个人复制和粘贴该淫秽代码,那是值得的。
更新:我想我不妨举一个例子,其中像 while(x>y);
这样的代码实际上非常有意义。
实际上,在 "good old" 的日子里,您经常会看到这样的代码。 咳 DOS。
虽然没有在线程上下文中使用。主要作为万一无法注册中断挂钩的后备方案(你们这些孩子可能会将其翻译为 "not possible to register an event handler")。
startAsynchronousDeviceOperation(..);
这几乎可以是任何东西,例如告诉硬盘通过 DMA 读取数据,或告诉声卡通过 DMA 记录,甚至可能调用不同处理器(如 GPU)上的函数。通常通过 outb(2)
.
之类的方式启动
while(byteswritten==0); // or while (status!=DONE);
如果与设备的唯一通信通道是共享内存,那就这样吧。不过,现在不希望在设备驱动程序和微控制器之外看到这样的代码。显然假设内存位置是最后写入的规格状态。
我正在查看 C++ class,其中包含以下几行:
while( x > y );
return x - y;
x
和y
是volatile int
类型的成员变量。我不明白这个结构。
我在这里找到了代码存根:https://gist.github.com/r-lyeh/cc50bbed16759a99a226。我想它不能保证是正确的甚至是有效的。
由于 x
和 y
已被声明为可变的,程序员期望它们将从程序外部更改。
在这种情况下,您的代码将保留在循环中
while(x>y);
并将 return 值从外部更改为 x-y
后 x <= y
。在您告诉我们更多关于您的代码以及您在哪里看到它之后,就可以猜到为什么要这样写的确切原因。本例中的 while
循环是一种等待其他事件发生的技术。
好像
while( x > y );
是一个spinning loop。直到 x <= y
才会停止。由于 x
和 y
是 volatile
,因此可以在此例程之外更改它们。因此,一旦 x <= y
变为真,将返回 x - y
。此技术用于等待某些事件。
更新
根据您补充的the gist,这个想法似乎是实现线程安全的无锁循环缓冲区。是的,实施不正确。例如,原始代码片段是
unsigned count() const {
while( tail > head );
return head - tail;
}
即使tail
变得小于等于head
,也不保证head - tail
returns是正数。调度程序可能会在 while
循环之后立即将执行切换到另一个线程,并且该线程可能会更改 head
值。无论如何,还有很多其他问题与读取和写入共享内存的工作方式(内存重新排序等)有关,所以请忽略这段代码。
volatile
关键字旨在防止某些优化。在这种情况下,如果没有关键字,编译器可以将您的 while
循环展开为具体的指令序列,这显然会破坏现实,因为可以在外部修改这些值。
想象一下:
int i = 2;
while (i-- > 0) printf("%d", i);
大多数编译器会查看这个并简单地生成对 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);
其他回复已经详细指出了指令的作用,但只是回顾一下,因为 y
(或链接示例中的 head
)被声明为 volatile
从不同的线程对该变量所做的更改将导致 while
循环在满足条件后完成。
然而,尽管 linked code example 很短,但它是一个近乎完美的例子,说明了如何 不 编写代码。
首先是行
while( tail > head );
会浪费大量的 CPU 周期,在满足条件之前几乎锁定一个核心。
随着我们的进步,代码会变得更好。
buffer[head++ % N] = item;
感谢 JAB 指出我在这里把 post- 误认为是 pre-increment。更正了含义。
由于没有 lock
s 或 mutex
es,我们显然必须假设最坏的情况。线程将在 item
中赋值之后和 head++
执行之前切换。然后 Murphy 将再次调用包含此语句的函数,在相同的 head
位置分配 item
的值。
之后 head
递增。现在我们切换回第一个线程并再次递增 head
。所以而不是
buffer[original_value_of_head+1] = item_from_thread1;
buffer[original_value_of_head+2] = item_from_thread2;
我们最终得到
buffer[original_value_of_head+1] = item_from_thread2;
buffer[original_value_of_head+2] = whatever_was_there_previously;
你可能会在客户端用很少的线程编写这样草率的代码,但在服务器端这只能被认为是一个滴答作响的定时炸弹。请改用 lock
s 或 mutex
es 等同步结构。
好吧,为了完整起见,行
while( tail > head );
在方法中 pop_back()
应该是
while( tail >= head );
除非您希望能够比实际推入的元素多弹出一个元素(或者甚至在推入任何内容之前弹出一个元素)。
很抱歉写下基本上可以归结为长篇大论的内容,但如果这只能阻止一个人复制和粘贴该淫秽代码,那是值得的。
更新:我想我不妨举一个例子,其中像 while(x>y);
这样的代码实际上非常有意义。
实际上,在 "good old" 的日子里,您经常会看到这样的代码。 咳 DOS。
虽然没有在线程上下文中使用。主要作为万一无法注册中断挂钩的后备方案(你们这些孩子可能会将其翻译为 "not possible to register an event handler")。
startAsynchronousDeviceOperation(..);
这几乎可以是任何东西,例如告诉硬盘通过 DMA 读取数据,或告诉声卡通过 DMA 记录,甚至可能调用不同处理器(如 GPU)上的函数。通常通过 outb(2)
.
while(byteswritten==0); // or while (status!=DONE);
如果与设备的唯一通信通道是共享内存,那就这样吧。不过,现在不希望在设备驱动程序和微控制器之外看到这样的代码。显然假设内存位置是最后写入的规格状态。