C++의 휘발성 한정자
이 기사에서는 C++의volatile
한정자를 소개합니다.
volatile
한정자를 사용하여 C++의 다른 스레드 또는 외부 작업에 의해 수정되는 개체를 나타냅니다
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;
}
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 Facebook