C++의 volatile은 동시성과 상관 없다

volatile은 C++에서 가장 오해받고 있는 키워드 중 하나일 것이다. 오해받는 이유는 크게 2개라고 생각한다. 첫 번째로 C++의 volatile이 Java나 C# 등 다른 언어에서 말하는 volatile과 다르기 때문이고, 두 번째로 C++의 volatile은 CPU와 컴파일러가 어떻게 동작하는지 알지 않으면 이해하기 어려운 비직관적인 기능이기 때문이라고 생각한다.

Java나 C# 등 다른 언어에서 volatile은 다른 스레드에서 visibility를 보장해주기 위해 사용된다. 따라서 변수의 접근이 리오더링 되는 것을 막고, 언제나 최신 값을 유지하는 것을 보장해준다. 특히나 멀티 스레드 프로그래밍을 한다면 누구나 한 번쯤 읽어본다는 고전 명작 The Art of Multiprocessor Programming에서 그 예제 코드가 Java로 돼 있기 때문에 C++에서도 volatile 키워드가 같은 의미를 가진다고 생각하는 사람이 많다.

하지만 C++에서 volatile은 멀티 스레드와 아무런 관련이 없다. 이는 과거 C 표준에 스레드와 관련된 내용이 없던 시절에 추가된 이후로 지금까지 스펙이 변경되지 않았기 때문이다. 그래서 C++의 volatile은 싱글 스레드 코드에서 변수의 접근이 최적화되어 사라지는 것을 막을 뿐이다. 예를 들어 int a가 있을 때 a += 1을 반복하는 코드가 3회 반복될 때, 이 중간에 a에 접근하는 코드가 없으면, 이 코드는 a += 3으로 최적화될 수 있다. 만약 중간에 a에 접근하는 코드가 있어도, 매번 a를 메모리에서 접근하지 않고, a를 레지스터에 집어넣어 최적화할 수 있다. 하지만 a의 타입이 volatile int였다면, 이와 같은 최적화는 할 수 없고 a를 메모리에서 읽어 1을 더하여 메모리에 쓰는 코드가 3번 들어가야 한다.

하지만 리오더링 되는 것을 막지 않는다. 따라서 의존성이 없는 두 변수가 volatile로 선언돼 있을 때 이 두 변수 사이에 읽기/쓰기는 다른 순서로 실행될 수 있다. 아래의 코드가 volatile이 리오더링되는 것을 막지 않기 때문에 C++이 volatile을 사용했을 때 다른 언어와 다르게 동작하는 대표적인 예제다.

위의 코드에서 thread1_mainthread2_main이 다른 스레드에서 동시에 실행될 때 코드가 쓰여 있는 순서대로 실행된다면 b가 1이고 a가 0인 순간이 올 수 없기 때문에, 절대로 assert에 걸리지 않는다. 하지만 앞에서 말했듯이 C++의 volatile은 리오더링을 막지 않기 때문에 thread1_mainb = 1a = 1보다 먼저 실행될 수 있어서 b는 1이지만 a는 0인 순간이 올 수 있고 assert에 걸릴 수 있다.

댓글

  1. Mspy는 사용자가 Google 행아웃에서 연결된 사람들의 대화와 활동을 감시할 수 있는 앱입니다. 또한 사용자는 행 아웃 스파이앱에서 대상 사람이 연결된 사람들을 감시할 수 있습니다. 이 앱은 Android 및 iOS 기기에서 사용할 수 있습니다.

    답글삭제

댓글 쓰기

이 블로그의 인기 게시물

[C++] enum class - 안전하고 쓰기 쉬운 enum

Log Aggregator 비교 - Scribe, Flume, Fluentd, logstash

RAII는 무엇인가

[Python] cache 데코레이터로 최적화하기

[Web] SpeechSynthesis - TTS API