sleepyotter
Interlocked 본문
for(int i=0; i<100000; ++i)
{
number++;
}
for(int i=0; i<100000; --i)
{
number--;
}
2개의 for문을 2개의 스레드에서 동시에 시작한 후에 작업이 끝난 후 number를 출력해보면 0이 나오지 않는다.
number++ 하는 부분은 실제로는 3단계로 이루어진다.
1. number의 값을 추출하여 다른 곳에 저장
2. 다른 곳에 저장한 값을 1증가
3. number에 다른 곳에 저장한 값을 대입
그러면 ++ 하는 쪽에서는 1번을 이제야 막 했는데, -- 하는 쪽에서는 우연찮게 루프를 여러번 돌아서 -5를 했다고 하자.
그러면 0에서 시작했을 때, -5 상태인데, ++하는 쪽에서는 0에 +1을 한 다음에, 3번을 해서 1이 되버린다.
즉, -4가 되어야 하는데 1이 되버렸다.
따라서 중요한 점은 일련의 작업이 오롯이 한 번에 처리되던가, 그게 아니라면 실패를 해야 한다. 이를 원자성(Atomic)이라 한다.
경합 조건(Race Condition)
어떤 변수를 처리하려고 서로 막 달려드는 상태이다. Race Condition이 발생할 때에는 원자성이 보장되거나 Lock을 사용하지 않으면 의도한 결과가 나오지 않는다.
이를 위해, Interlocked가 c#에서 제공된다.
number++ 하는 부분은 아래와 같이 바꿀 수 있다.
Interlocked.Increment(ref number)
interlocked를 모르는 채로 보면 ref를 넘겨주기 때문에 위에 설명한 상황 처럼 --를 5번 해서 -5가 됐다고 쳐도 ++하려는 때에 해당하는 값에 +1 해주기 때문에 정상적으로 -4가 된다.
정확한 메커니즘은, 이에 접근한 순간 다른 녀석들은 이 값에 접근하지 못하도록 막고 내가 작업을 끝낸 후에야 다른 녀석들이 접근할 수 있게 하는 것이다. 또, 내부적으로 MemoryBarrier를 사용하기 때문에 volatile은 사용하지 않아도 된다.