내용 보기

작성자

관리자 (IP : 172.17.0.1)

날짜

2020-07-13 04:53

제목

[C#] Interlocked.CompareExchange 사용 예제


한 번만 실행하려고 하는 경우 간단하게는 다음과 같은 처리를 할 수 있습니다.

int _value = 0;
bool _done = false;

void func()
{
if (_done == false)
{
_value++;
_done = true;
}
}


당연하지만, 이는 thread-safe하지 않기 때문에 다중 스레드에서 위의 func을 호출하려면 예기치 않은 동작이 발생할 수 있습니다. 위의 상황에서는 _value 값이 2 이상이 나올 수 있는데 이는 다음과 같은 코드로 테스트해 볼 수 있습니다.

using System;
using System.Collections.Generic;
using System.Threading;

class Program
{
static int _value = 0;
static bool _done = false;

static void Main(string[] args)
{
int count = 0;

while (true)
{
_value = 0;
_done = false;

List<Thread> list = new List<Thread>();
for (int i = 0; i < 100; i++)
{
Thread t = new Thread(threadNotSafeFunc);
list.Add(t);
}

foreach (var item in list)
{
item.Start();
}

foreach (var item in list)
{
item.Join();
}

if (_value != 1)
{
throw new ApplicationException("_value: " + _value);
}

Console.WriteLine(count++ + ": Tested - " + _value);
}
}

private static void threadNotSafeFunc()
{
Thread.Sleep(1000);

if (_done == false)
{
_value++;
_done = true;
}
}
}


실행해 보면, 여지없이 ApplicationException 예외가 발생합니다. 이를 위해 lock 구문을 써도 되는데, 쓸데없이 참조 형 변수를 하나 둬야 하므로 이럴 땐 Interlocked.CompareExchange를 써 볼 수 있습니다. 원래는 다음과 같이 코딩할 수 있을 텐데,

int _value = 0;
bool _done = false;

void threadSafeFunc()
{
if (Interlocked.CompareExchange(ref _done, true, false) == false)
{
_value++;
}
}


아쉽게도 위의 코드를 컴파일하면 다음과 같은 오류가 발생합니다.

Error CS0452 The type 'bool' must be a reference type in order to use it as parameter 'T' in the generic type or method 'Interlocked.CompareExchange<T>(ref T, T, T)


bool 타입을 지원하지 않는 것인데, 사실 좀 이해가 안 됩니다. 어차피 CPU의 워드 타입 내에서 처리할 수 있는 형식이라서 .NET BCL 측에서 제공해줘도 무방할 텐데 굳이 뺀 이유를 모르겠습니다. 이 때문에, bool 타입은 그냥 true를 1로, false를 0으로 해석하는 전통적인 방식에 기인해서 int 타입을 써 다음과 같이 처리해 주시면 됩니다.

int _value = 0;
int _intDone = false;

void threadSafeFunc()
{
if (Interlocked.CompareExchange(ref _intDone, 1, 0) == 0)
{
_value++;
}
}


ApplicationException이 발생했던 이전 예제의 threadNotSafeFunc을 threadSafeFunc으로 변경해서 처리하면 예외 없이 잘 실행되는 것을 확인할 수 있습니다.

출처1

http://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&pageno=0&detail=1&wid=11128

출처2