티스토리 뷰
[Effective Modern C++] 항목 40. 동시성에는 std::atomic을 사용하고, volatile은 특별한 메모리에 사용하라
pppgod 2020. 2. 25. 23:34프로그래머들은 종종 volatile 과 std::atomic 을 혼동한다. 그러나 이 두 개는 서로 전혀 다른 역할을 한다. std::atomic 은 원자성을 보존해주기 위해서 사용된다. 다시 말하자면 mutex 없이도 두 개의 스레드가 동시에 접근해도 data race 문제가 발생하지 않는다. volatile 은 컴파일러가 최적화하며 특정 라인을 수행하지 않는 경우가 없도록 해준다. 코드로 살펴보자.
volatile int vi(0);
++vi;
--vi;
std::cout << vi;
위 코드의 출력 결과는 당연히 0이다. 값은 초기와 다르지 않다. 우리가 생각하기에도 무의미한 계산을 한 것이다. 컴파일러는 최적화를 하며 두 개의 라인을 무시할 수 있다. 그런데 만약 임베디드나 특정한 상황에서 저 계산식은 무시가 되면 안되는 경우가 발생한다. 그런 경우에 사용하는 것이 volatile 이다. 컴파일러가 무시할 가능성을 없애주는 것이다. 그렇다고해서 data race 문제는 해결되지 않는다. 두 개의 스레드가 동시에 접근은 가능하다는 의미이다. 반대로 std::atomic 에서는 무의미한 계산을 무시할 수 있다.
코드 재배치
또다른 차이점으로는 코드 재배치라는 컴파일러 최적화가 있다. 컴파일러는 코드 라인의 순서를 임의대로 바꿀 수 있다. 레지스터에 적재하고 내리는데 사용하는 시간을 최적화하기 위해서 말이다. 그런데 std::atomic 에서는 이야기가 달라진다. std::atomic 을 기록하는 라인이 수행될 때는 그 이전에 나타난 라인들이 수행되어서는 안된다. 이것은 std::atomic 의 특성이다. 다시 말하자면 volatile 은 그럴 필요가 없다는 것이다. 코드 재배치로 라인이 변경될 수 있다.
std::atomic
좀 더 std::atomic 의 특징들을 살펴보자. std::atomic 은 복사와 이동 연산을 지원하지 않는다. 그 이유는 간단하다. 원자성을 보존해야 하는게 주 목적인데 복사를 위해 읽고 기록하는 작업을 원자적으로 계산하는 하드웨어는 지원하지 않기 때문이다. 다행히 복사를 하는 방법이 가능하다.
std::atomic<int> y(x.load());
y.store(x.load());
레지스터를 이용하여 최적화도 가능한데 C++17 부터는 의미가 없기 때문에 넘어가도록 하겠다.
결론
우리는 std::atomic 과 volatile 의 차이점에 대해서 알아보았다. 이 두 개는 서로 전혀 다른 역할을 한다는 것을 우리는 알 수 있었다. 따라서 두 개는 한꺼번에 사용도 가능하다.
volatile std::atomic<int> val;
참고 서적
스콧 마이어스, Effective Modern C++
'C++ > Effective Modern C++' 카테고리의 다른 글
[Effective Modern C++] 항목 42. 삽입 대신 생성 삽입을 고려하라 (0) | 2020.03.05 |
---|---|
[Effective Modern C++] 항목 41. 이동이 저렴하고 항상 복사되는 복사 가능 매개변수에 대해서는 값 전달을 고려하라 (0) | 2020.03.03 |
[Effective Modern C++] 항목 39. 단발성 사건 통신에는 void 미래 객체를 고려하라 (0) | 2020.02.24 |
[Effective Modern C++] 항목 37. std::thread들을 모든 경로에서 합류 불가능하게 만들어라 (0) | 2020.02.09 |
[Effective Modern C++] 항목 36. 비동기성이 필수일 때에는 std::laynch::async를 지정하라 (0) | 2020.02.02 |
- Total
- Today
- Yesterday
- thread
- detach
- Effective Modern C++
- C++11
- Future
- auto
- forward
- CPP
- async
- 보편 참조
- std::forward
- const
- C++
- 포인터
- std::move
- 람다
- Join
- Modern
- Effective
- Overloading
- Perfect
- C++14
- Unreal
- Forwarding
- 보편참조
- Override
- 발아시기
- MOVE
- C
- 다이소
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |