멀티쓰레드 개론
쓰레드
공유 - heap영역, static
개별 - stack영역
쓰레드가 동시에 공유 영역에 접근하면 문제가 된다!
쓰레드 생성
c# Thread는 IsBackground = false가 default다
즉, 디폴트는 forward에서 thread가 동작한다
백그라운드 thread는 메인스레드가 꺼지면 자동으로 꺼진다!
ThreadPool 에 있는 쓰레드는 background로 동작한다
ThreadPool을 사용할 때는 가급적 짧은 일을 주는것이 좋다. 풀이 사용할 수 있는 스레드는 제한적이기 때문이다. 스레드 풀은 n명이 대기중인 인력사무소에서 알바를 고용하는 것에 비유할 수 있다. 알바는 주어진 일이 끝나면 인력사무소로 되돌아간다.
Task에 LoogRunning 옵션을 추가하면 TreadPool에서 스레드를 뽑아 쓰는게 아니라 별도의 스레드를 고용해서 해당 task를 수행하게 한다
Task에 LoogRunning옵션을 추가하지 않으면 TreadPool에서 스레드를 뽑아 쓰게 된다
컴파일러 최적화
전역, heap영역은 쓰레드가 동시 접근이 가능하다! => 충돌
Tread.Sleep(1000) : 1초 동안 멈춰있어라
task는 Wait()라는 함수가 있다
비쥬얼 스튜디오 Debug 모드와 release모드
게임을 배포할 때는 release모드로 되는데, 컴파일러가 최적화 시킨것이 내가 의도한 것과 다르게 작동되기도 함. 그 예시!
volatile 키워드: 휘발성 data! 언제 바뀔지 모르는 데이터니 최적화하지 말아라!
c++과 c#에서 volatile 키워드는 다르게 동작한다! c#에서는 c++에서 제공하는 것보다 더 확장된 개념이기 때문에 개발자에게 혼동을 준다. 사용하지 않을것을 추천한다
캐시 이론
DataTime.Now.Ticks; 시간을 불러온다
메모리 배리어
하드웨어도 최적화를 해주고있다!
=> 명령어의 순서를 뒤바꿔서 실행할 수 있다!
메모리 베리어
1. 코드 재배치 억제
2. 가시성
1. Full Memory Barrier : Store/Load 둘다 막는다
ASM(어셈) - MFENCE
C# - Thread.MemoryBarrier
2. Store Memory Barrier: Store만 막는다
ASM - SFENCE
3. Load Memory Barrier: Load만 막는다
ASM - LFENCE
Write하고 나서 Barrier(동기화 한다고 생각)
Barrier하고 나서 Load
Interlocked
[race condition] 경합 조건
스레드들이 차례를 지키지 않고 일을 처리하다보니 중복된 일을 수행하기도 함
c# - number++
ASM -
mov tmp, [number주소]
inc tmp
mov [number주소], tmp
atomic = 원자성 : 더이상 쪼개지면 안되는 최소 단위
즉, number++이 세단계로 이루어지긴 하지만, 한번에 이루어 지는걸로 처리되어야 하기때문에, 각 세단계는 쪼갤 수 없는 단위로 취급하여야한다
원자성 예시)
검을 구매하는 것:
1. 골드-=100,
//여기서 서버가 다운되면 끔찍한 오류!
2. 인벤+=검
// 1, 2과정 동시에 처리되어야 함.
Interlocked.Increment(ref number) : ++ =>원자성 보장!
Interlocked.Decrement(ref number); --
ㄴ매개변수에 ref가 붙은것에 유의하여야한다
Lock 기초
읽는 작업은 문제가 되지 않지만 쓰는 작업은 문제가 될 수있다
해결1
Monitor.Enter(object);
Monitor.Exit(object);
한칸짜리 화장실이라고 생각하자! 사람이 들어가면 나올때까지 기다렸다가 들어가야한다
Monitor의 문제점
return이나 오류발생 등의 이유로 Monitor.Exit()이 실행되지 않을 경우
해결2
try{Moniter.Enter(obj); number++; return; }
finally{ Moniter.Exit(obj); }
해결3
lock(obj){ number++; }
SpinLock
[spin lock] 멀티스레드 관련 면접1등 질문
스핀락 Interlocked를 사용하여 구현!
int original = Interlocked.Exchange(ref _lock, 1);
if(original == 0) break;
original은 _lock이 변경되기 이전에 값! 만약 변경전이 0이라면 1로 바꿔주는 행위를 완료하고, 만약 변경 전이 1이라면 누군가 lock을 사용중이니 lock이 0이 될 때까지 기다린다
댓글