Memory model에 대한 정책 세 가지 (더 있으나 보편적으로 세 가지를 주로 사용)
1) Sequentially consistent (seq_cst)
2) Acquire-Releasse (acquire, release)
3) Relaxed (relaxed)
위에 있을 수록 더 엄격한 정책, 컴파일러의 최적화가 적다.
아래로 갈 수록 컴파일러 최적화의 여지가 많다.
예시 코드
atomic<bool> ready;
int32 value;
void Producer()
{
value = 5;
ready.store(true, memory_order::메모리 정책);
}
void Consumer()
{
while(ready.load(memory_order::메모리 정책) == false);
cout << valule << endl;
}
int main()
{
ready = false;
value = 0;
thread t1(Producer);
thread t2(Consumer);
t1.join();
t2.join();
return 0;
}
만약 메모리 정책이 memory_order_relaxed로 설정할 경우, 컴파일러 최적화의 영향을 많이 받기 때문에 첫번째 순서 바꾸는 최적화 등의 영향을 받을 수 있다. 즉 value = 5; 라인이 ready.store( .. ) 라인 밑으로 내려가버리는 참사가 발생할 수도 있다.
acquire - release의 경우, 우선 애초에 사용할 때 Producer 측에서는 memory_order::memory_order_acquire를 사용하고, Consumer 측에서는 memory_order::memory_order_release 를 사용해 주어야 한다. (짝을 맞춰서 써야 함)
그리고 컴파일러 영향을 중간정도 받기 때문에 relaxed와는 다르게 코드 순서가 재배치되는 것은 방지된다. (이때 자기 기준 위 아래 줄만 재배치를 막는 것이지, 같은 스코프 내의 다른 코드들은 영향받지 않는다)
void Producer()
{
value = 5;
ready.store(true, memory_order::memory_release);
/ -------- 이 지점부터 ------- /
}
void Consumer()
{
/ ------ 다른 스레드에서 acquire 하는 순간까지 그 사이동안 메모리가 유지됨이 보장된다(가시성) ------ /
while(ready.load(memory_order::memory_acquire) == false);
cout << value << endl; // 즉 value는 5임이 보장된다
}
추가로 위 코드처럼 가시성 또한 보장된다.
메모리 정책이 memory_order_seq_cst로 설정되있는 경우, 위에 등장한 모든 부분들을 보장해준다. (코드 재배치, 가시성)
AMD나 intel CPU는 atomic 연산이 공백으로 정책을 별도 지정해주지 않았을 경우 seq_cst가 기본값으로 사용된다. 즉 대부분의 경우 seq_cst를 사용하는 게 성능 면에서도 큰 차이가 없고 안전하다.
(단 ARM의 경우에는 유의미한 차이가 있다고 한다)
꼭 atomic 객체를 사용하지 않더라도 atomic_thread_fence를 이용해 동일한 효과를 얻을 수 있다. 사진에서처럼 realse와 acquire 두 지점을 기준으로 가시성 및 코드 재배치 방지가 보장(Thread 2에서 fence를 친 이후 result.tick에 값이 대입된다는 이 코드 순서가 변하지 않는다)되는 것을 알 수 있다.
참고글:
http://egloos.zum.com/sweeper/v/3059861
http://en.wikipedia.org/wiki/Memory_barrier
https://www.kernel.org/doc/Documentation/memory-barriers.txt
https://en.cppreference.com/w/cpp/atomic/memory_order
'os > multithreading, parallel computing' 카테고리의 다른 글
lock-free atomic calculation (0) | 2022.09.10 |
---|---|
멀티스레드 환경에서 발생하는 문제들 (0) | 2022.09.10 |
스레드의 race condition (0) | 2021.05.26 |
쓰레드와 프로세스, CPU 병렬 처리와 동시 처리 (0) | 2021.01.03 |