C++ 中的 volatile 限定符
本文将介绍 C++ 中的 volatile
限定符。
在 C++ 中使用 volatile
限定符来表示被另一个线程或外部操作修改的对象
volatile
关键字行为通常应被视为依赖于硬件,并且用户空间应用程序开发人员应始终查阅特定的编译器手册,了解如何在各种情况下解释限定符。通常,volatile
关键字通知编译器给定的对象不应该针对加载操作进行优化,并且总是从主内存而不是寄存器或缓存中检索。请注意,有多个缓存层次结构,软件通常无法访问这些层次结构,仅在硬件中进行管理,但是当编译器尝试加载寄存器中的内存位置时,它会自动被缓存。因此,对同一内存位置的后续访问可以从靠近 CPU 的高速缓存行完成,并且比 RAM 快数倍。
同时,如果对象被某些外部信号或类似中断的例程修改,则应从 RAM 访问更改的值,因为缓存的值不再有效。因此,对 volatile
对象的访问由编译器相应地处理。为了演示可能的场景,我们实现了一个修改全局 volatile
整数变量的函数和另一个在 while
循环语句中计算相同整数的函数。注意 while
循环可以有一个空的主体来让这个例子工作。首先,main
函数创建一个单独的线程来执行 IncrementSeconds
函数。紧接着,主线程调用 DelayTenSeconds
函数,该函数进入循环,如果 seconds
变量不超过 10
的值,该循环不会返回。由于另一个线程已经同时开始增加 seconds
变量,主线程将很快观察到更改后的值并从函数返回。
#include <iostream>
#include <unistd.h>
#include <thread>
using std::cout; using std::endl;
using std::cerr; using std::cin;
volatile int seconds = 0;
void DelayTenSeconds() {
while (seconds < 10) {
usleep(500000);
cerr << "waiting..." << endl;
}
}
void IncrementSeconds() {
for (int i = 0; i < 10; ++i) {
sleep(1);
cerr << "incremented " << endl;
seconds = seconds + 1;
}
}
int main() {
struct timeval start{};
struct timeval end{};
std::thread th1;
th1 = std::thread(IncrementSeconds);
DelayTenSeconds();
th1.join();
return EXIT_SUCCESS;
}
输出:
waiting...
incremented
waiting...
....
waiting...
10.002481 sec
因此,我们基本上实现了一个条件延迟函数,它会等待 volatile
对象被外部操作修改。现在,人们可能会注意到,如果删除 volatile
限定符并使用常规全局变量,则此代码的行为将完全相同,但不应忘记修改代码块可能来自不同的翻译单元或外部信号操作。后一种情况将迫使编译器优化 DelayTenSeconds
循环,因为当前翻译单元中未修改该变量。
#include <iostream>
#include <unistd.h>
#include <thread>
#include <sys/time.h>
using std::cout; using std::endl;
using std::cerr; using std::cin;
volatile int seconds = 0;
void DelayTenSeconds() {
while (seconds < 10) {
usleep(500000);
cerr << "waiting..." << endl;
}
}
float TimeDiff(struct timeval *start, struct timeval *end){
return (end->tv_sec - start->tv_sec) + 1e-6*(end->tv_usec - start->tv_usec);
}
void IncrementSeconds() {
for (int i = 0; i < 10; ++i) {
sleep(1);
cerr << "incremented " << endl;
seconds = seconds + 1;
}
}
int main() {
struct timeval start{};
struct timeval end{};
std::thread th1;
th1 = std::thread(IncrementSeconds);
gettimeofday(&start, nullptr);
DelayTenSeconds();
gettimeofday(&end, nullptr);
printf("%0.6f sec\n", TimeDiff(&start, &end));
th1.join();
return EXIT_SUCCESS;
}
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn