일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- PCCE
- boj 1958
- lock based queue
- boj 6443
- 색종이와가위
- DirectX12
- boj 21921
- 비밀 코드 해독
- LCS
- DirectX
- 지게차와 크레인
- boj 15724
- c++
- 브루트포스
- orthographic projection
- pccp 기출문제 풀이
- render target
- pcce 기출문제 풀이
- lock based stack
- dp
- boj 1074
- lock free stack
- boj 20207
- tessellation
- 프로그래밍공부
- boj 22942
- boj 11053
- 홀짝트리
- 데이터 체커
- 2025 프로그래머스 코딩챌린지 1차예선
- Today
- Total
오구의코딩모험
[Server] SpinLock, Sleep, Event 본문
서버 개발에서 배우는 동기화 기법
서버 개발에서는 성능 최적화와 동기화를 위한 다양한 기법이 사용됩니다.
이번 글에서는 volatile, SpinLock, Sleep, Event (Auto Reset Event / Manual Reset Event) 등의 학습한 내용을 정리합니다.
int32 a=0;
a = 1;
a = 2;
a = 3;
a = 4;
1. volatile: 컴파일러 최적화 방지
volatile이란?
- volatile 키워드는 컴파일러의 최적화를 방지하는 기능을 합니다.
- 일반적인 코드에서 불필요한 대입 연산이 제거되지만, volatile을 선언하면 모든 연산이 실행됩니다.
예제 개념
- 변수 a가 여러 번 대입되는 경우, 컴파일러는 불필요한 연산을 제거하여 최적화합니다.
- 하지만 volatile을 사용하면 모든 대입이 유지되며, 실시간 데이터 변경이 필요한 경우 유용합니다.
class SpinLock
{
public:
void lock()
{
// CAS (Compare-And-Swap)
bool expected = false;
bool desired = true;
// CAS 의사 코드
//if (_locked == expected)
//{
// expected = _locked;
// _locked = desired;
// return true;
//}
//else
//{
// expected = _locked;
// return false;
//}
while (_locked.compare_exchange_strong(expected, desired) == false)
{
expected = false;
}
}
void unlock()
{
// _locked = false;
_locked.store(false);
}
private:
atomic<bool> _locked = false;
};
2. SpinLock: 빠른 락 구현
SpinLock이란?
- SpinLock은 하나의 스레드만 점유할 수 있도록 락을 구현하는 방식입니다.
- atomic 연산을 사용하여 스레드가 lock을 획득할 때까지 반복 시도합니다.
- CAS(Compare-And-Swap) 기법을 활용하여 lock을 atomic하게 획득합니다.
SpinLock의 특징
- 컨텍스트 스위칭 없이 유저 모드에서 반복 실행되므로, 빠르게 lock을 해제할 경우 유리합니다.
- 그러나 긴 시간이 걸리는 작업에서는 CPU 점유율이 높아지는 단점이 있습니다.
// (= this_thread::sleep_for(std::chrono::milliseconds(100)); )
this_thread::sleep_for(100ms);
this_thread::yield();
3. Sleep과 Yield: 실행 소유권 반환
Sleep이란?
- Sleep은 실행 소유권을 반납하고, 지정한 시간 동안 재스케줄링이 이루어지지 않도록 합니다.
- this_thread::sleep_for(100ms)와 같은 함수로 사용됩니다.
Yield란?
- Yield는 현재 타임슬라이스에서 실행할 필요가 없을 때 실행 소유권을 반환합니다.
- 즉시 재스케줄링이 가능하지만, 현재 시점에서 실행할 필요가 없다고 판단될 경우 반환됩니다.
- this_thread::yield()를 사용하며, sleep_for(0ms) 와 유사한 효과를 가집니다.
Sleep vs Yield 비교
- Sleep : 일정 시간 동안 재스케줄링을 방지하며, 일정 시간 동안 실행을 중단.
- Yield : 즉시 실행을 중단하고 다른 스레드에게 CPU를 양보할 수 있도록 함.
// 커널 오브젝트
// Usage Count
// Signal / Non-Signal << bool
// Auto / Manual << bool
handle = ::CreateEvent(NULL/*보안속성*/, FALSE/*bManualReset*/, FALSE/*bInitialState*/, NULL);
::CloseHandler(handle);
4. Event (Auto Reset Event / Manual Reset Event)
Event란?
- Event는 특정 작업이 실행 가능할 때 시그널을 보내 실행을 유도하는 기법입니다.
- 커널 개입이 필요하므로 추가적인 비용이 발생하지만, 정확한 제어가 가능합니다.
Event의 특징
- Windows API의 CreateEvent를 사용하여 생성됩니다.
- SetEvent를 이용하여 시그널을 보내고, WaitForSingleObject를 통해 시그널 변경을 확인합니다.
- 사용 후 CloseHandle을 호출하여 자원을 정리해야 합니다.
Auto Reset Event vs Manual Reset Event
- Auto Reset Event
* 시그널이 감지된 후 자동으로 Non-Signal 상태로 변경됩니다.
* 스레드가 이벤트를 처리한 후, 다른 스레드가 다시 실행되도록 설계됨.
- Manual Reset Event
* 시그널을 수동으로 변경해야 합니다.
* 여러 스레드가 같은 이벤트를 감지하도록 유지할 수 있음.
정리
1. volatile 사용 시 주의
- 메모리 최적화를 방지하지만, 모든 연산이 실행되므로 과도한 사용은 피해야 합니다.
2. SpinLock 활용
- 빠르게 lock을 해제할 경우 효과적이지만, 장시간 점유 시 CPU 부하가 증가할 수 있습니다.
3. Sleep과 Yield 적절한 선택
- Sleep은 명확한 시간 대기 시 사용하고, Yield는 불필요한 타임슬라이스 낭비를 줄이는 용도로 사용합니다.
4. Event 사용 시 고려할 점
- 커널 개입이 필요하여 자원 소모가 발생하므로, 너무 자주 호출하지 않도록 설계해야 합니다.

'Game > Server' 카테고리의 다른 글
[Server] Lock Free Stack (上편) (0) | 2025.04.02 |
---|---|
[Server] 메모리 모델, Thread Local Storage (= TLS), Lock-Based Stack/Queue (0) | 2025.03.31 |
[Server] Condition Variable, Future (0) | 2025.03.28 |
[Server] Game Server, Multi Thread, Atomic, Lock, Dead Lock (0) | 2025.01.16 |