C++ 中的 volatile 限定符
本文將介紹 C++ 中的 volatile
限定符。
在 C++ 中使用 volatile
限定符來表示被另一個執行緒或外部操作修改的物件
volatile
關鍵字行為通常應被視為依賴於硬體,並且使用者空間應用程式開發人員應始終查閱特定的編譯器手冊,瞭解如何在各種情況下解釋限定符。通常,volatile
關鍵字通知編譯器給定的物件不應該針對載入操作進行優化,並且總是從主記憶體而不是暫存器或快取中檢索。請注意,有多個快取層次結構,軟體通常無法訪問這些層次結構,僅在硬體中進行管理,但是當編譯器嘗試載入暫存器中的記憶體位置時,它會自動被快取。因此,對同一記憶體位置的後續訪問可以從靠近 CPU 的快取記憶體行完成,並且比 RAM 快數倍。
同時,如果物件被某些外部訊號或類似中斷的例程修改,則應從 RAM 訪問更改的值,因為快取的值不再有效。因此,對 volatile
物件的訪問由編譯器相應地處理。為了演示可能的場景,我們實現了一個修改全域性 volatile
整數變數的函式和另一個在 while
迴圈語句中計算相同整數的函式。注意 while
迴圈可以有一個空的主體來讓這個例子工作。首先,main
函式建立一個單獨的執行緒來執行 IncrementSeconds
函式。緊接著,主執行緒呼叫 DelayTenSeconds
函式,該函式進入迴圈,如果 seconds
變數不超過 10
的值,該迴圈不會返回。由於另一個執行緒已經同時開始增加 seconds
變數,主執行緒將很快觀察到更改後的值並從函式返回。
#include <unistd.h>
#include <iostream>
#include <thread>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
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 <sys/time.h>
#include <unistd.h>
#include <iostream>
#include <thread>
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
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;
}