본문 바로가기
cs공부/운영체제

운영체제 - Interlocked(Increment,Exchange,Add메서드 등)

by 코딩하는 돼징 2023. 5. 15.
반응형

Interlocked

다중 쓰레드에서 공유하는 변수에 대한 원자 단위 연산을 제공

장점

RaceCondition 알아보러 가기

 

운영체제 - RaceCondition, Atomic

RaceCondition 여러개의 쓰레드들이 공유 변수 동시 접근 시 실행 순서에 따라 결과값이 달라지는 문제 static int number = 0; static void Thread_1() { for (int i = 0; i < 100000; i++) number++; } static void Thread_2() { for (i

code-piggy.tistory.com

연산 순서 보장하여RaceCondition을 방지할 수 있고 원자적 연산을 수행한다.

단점

연산 순서가 보장되어 있어서 느릴 수 있다.

 


int number가 아니라 ref int number를 사용하는 이유

Interlocked.Increment(ref number)

number의 메모리 주소를 참조하여 해당 주소에 있는 값을 증가키신다. 메모리 주소를 참조하여 값을 증가시키기 때문에 다른 쓰레드에서 변수에 접근하더라도 해당 메모리 주소의 값을 조작하더라도 문제가 발생하지 않는다.

Interlocked.Increment(number)

number의 값을 복사하여 증가시키고 그 값을 다시 변수에 할당한다. 변수의 값을 복사하여 연산을 수행하기 때문에 다른 쓰레드에서 변수에 접근하는 경우 값이 동기화 되지 않을 수 있다. 그러면 RaceCondition이 발생할 수 있다.

 

결과적으로 Interlocked.Increment(ref number)을 사용하면 RaceCondition을 방지하는 원자적 연산이 사용되어지면서 안전하게 연산을 할 수 있다.


01 Interlocked.Increment 

원자 단위 연산으로 지정된 변수의 값을 증가시키고 결과를 저장

public static int Increment (ref int location);

 

02 Interlocked.Decrement 

원자 단위 연산으로 지정된 변수를 감소시키고 결과를 저장

public static int Decrement (ref int location);

Code 예시

static int location1 = 0;
static int value = 1;
static int value2 = -1;

static void Thread_1()
{
    for (int i = 0; i < 100000; i++)
        Interlocked.Increment(ref number);
}
static void Thread_2()
{
    for (int i = 0; i < 100000; i++)
        Interlocked.Decrement(ref number);
}

결과

원리

동시다발적으로 실행을 해도 먼저 실행된 것이 있을 것이다. 만약 Thread_1이 시작되었다면 number = 0 에서 1로 바뀌고 Thread_1이 끝나기 전까지는 Thread_2가 실행되지 못한다.  그리고 Thread_2가 실행되면 number = 1에서 0으로 바뀐다.

순서 보장이 생기는 것이다. 이러한점이 장점이 될 수 있지만 원래 number++, number--보다 느리다는 단점이 있다.


03 Interlocked.Exchange

변수의 값을 지정 값으로 교체

public static float Exchange (ref float location1, float value);

매개 변수

location1 - 지정된 값으로 지정할 변수

value - location1의 지정 값

반환

location1의 원래 값


Code 예시

static long location1 = 0;
static int value = 1;
static int value2 = -1;
static long comparand = 0;

static void Thread_1()
{
    long read1 = Interlocked.Exchange(ref location1, value);
}
static void Thread_2()
{
    long read2 = Interlocked.Exchange(ref location1, value2);
}

결과 1

실행 순서

1. Thread_1이 실행되었다. Thread_1에서 location1의 초기 값은 0이다.

2. Interlocked.Exchange를 호출하면 location1값은 value의 값으로 변경된다.

3. Interlocked.Exchange의 반환값은 location1의 원래 값이므로 0이 반환된다.

--------------------------------------------Thread_1실행 끝 -> read1 = 0, location1 = 1

4. Thread_2이 실행되었다. Thread_2에서 location1의 초기 값은 1이다.

5. Interlocked.Exchange를 호출하면 location1값은 value2의 값으로 변경된다.

6. Interlocked.Exchange의 반환값은 location1의 원래 값이므로 1이 반환된다.

--------------------------------------------Thread_2실행 끝 -> read2 = 1, location1 = -1


결과 2

실행 순서

1. Thread_2이 실행되었다. Thread_2에서 location1의 초기 값은 0이다.

2. Interlocked.Exchange를 호출하면 location1값은 value2의 값으로 변경된다.

3. Interlocked.Exchange의 반환값은 location1의 원래 값이므로 0이 반환된다.

--------------------------------------------Thread_2실행 끝 -> read2 = 0, location1 = -1

4. Thread_1이 실행되었다. Thread_1에서 location1의 초기 값은 -1이다.

5. Interlocked.Exchange를 호출하면 location1값은 value의 값으로 변경된다.

6. Interlocked.Exchange의 반환값은 location1의 원래 값이므로 -1이 반환된다.

--------------------------------------------Thread_1실행 끝 -> read1 = -1, location1 = 1


04 Interlocked.Read

원자단위 연산으로 로드된 값을 반환

public static long Read (ref long location);

매개 변수

lcoation - 로드 될 값

반환

로드된 값


Code 예시

static int value = 1;
static int value2 = -1;
static void Thread_1()
{
    long read1 = Interlocked.Read(ref value);
    Console.WriteLine(read1);
}
static void Thread_2()
{
    long read2 = Interlocked.Read(ref value2);
    Console.WriteLine(read2);
}


05 Interlocked.Add

원자 단위 연산으로 두 값을 더하기

public static int Add (ref int location1, int value);

매개 변수

location1 - 더할 첫 번째 값이 있는 변수

value - 더할 값

반환

location1에 저장된 새로운 값


Code 예시

static int location1 = 0;
static int value = 1;
static int value2 = -1;

static void Thread_1()
{
    for (int i = 0; i < 100000; i++)
    {
        Interlocked.Add(ref location1, value);
    }
}
static void Thread_2()
{
    for (int i = 0; i < 100000; i++)
    {
        Interlocked.Add(ref location1, value2);
    }
}

결과


06 Interlocked.CompareExchange

원자 단위 연산으로 두 값이 같은지 비교하여 같으면 첫번째 값을 바꿈

public static float CompareExchange (ref float location1, float value, float comparand);

매개 변수

location1 - comparand와 비교하여 바뀔 수 있는 값을 가진 대상

value - 비교 결과가 같은 경우 대상 값을 바꿀 값

comparand - location1의 값과 비교할 값

반환

location1의 원래 값

 

연산 순서

1. loaction1변수와 comparand 값을 비교

2. loaction1변수와 comparand 값이 같다면 location1에 value값 대입

3. loaction1변수와 comparand 값이 다르다면 location1값 반환


Code 예시

static long location1 = 0;
static int value = 1;
static int value2 = -1;
static long comparand = 0;

static void Thread_1()
{
    long read1 = Interlocked.CompareExchange(ref location1, value,comparand);
}
static void Thread_2()
{
    long read2 = Interlocked.CompareExchange(ref location1, value2, comparand);
}

결과 1

실행 순서

1. Thread_2이 실행되었다. Thread_2에서 location1의 초기 값은 0이다.

2. Interlocked.CompareExchange를 호출하면 location1과 comparand값이 같으므로 location1의 값이 value2의 값으로 변경된다.

3. Interlocked.CompareExchange의 반환값은 location1의 원래 값이므로 0이 반환된다.

--------------------------------------------Thread_2실행 끝 -> read2 = 0, location1 = -1

4. Thread_1이 실행되었다. Thread_2에서 location1의 초기 값은 -1이다.

5. Interlocked.CompareExchange를 호출하면 location1과 comparand값이 다르므로 location1의 값은 그대로이다.

6. Interlocked.CompareExchange의 반환값은 location1의 원래 값이므로 -1이 반환된다.

--------------------------------------------Thread_1실행 끝 -> read1 = -1, location1 = -1


결과 2

실행 순서

1. Thread_1이 실행되었다. Thread_1에서 location1의 초기 값은 0이다.

2. Interlocked.CompareExchange를 호출하면 location1과 comparand값이 같으므로 location1의 값이 value의 값으로 변경된다.

3. Interlocked.CompareExchange의 반환값은 location1의 원래 값이므로 0이 반환된다.

--------------------------------------------Thread_1 실행 끝 -> read1 = 0, location1 = 1

4. Thread_2이 실행되었다. Thread_1에서 location1의 초기 값은 1이다.

5. Interlocked.CompareExchange를 호출하면 location1과 comparand값이 다르므로 location1의 값은 그대로이다.

6. Interlocked.CompareExchange의 반환값은 location1의 원래 값이므로 1이 반환된다.

--------------------------------------------Thread_2 실행 끝 -> read2 = 1, location1 = 1

반응형

댓글