C++의 휘발성 한정자

Jinku Hu 2023년10월12일
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;
}
작가: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

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