Interlocked
다중 쓰레드에서 공유하는 변수에 대한 원자 단위 연산을 제공
장점
RaceCondition 알아보러 가기
연산 순서 보장하여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
'cs공부 > 운영체제' 카테고리의 다른 글
운영체제 - 임계구역,동기화(Monitor,try-finally,lock) (0) | 2023.05.16 |
---|---|
운영체제 - 프로세스 시스템 (0) | 2023.05.16 |
운영체제 - RaceCondition, Atomic (0) | 2023.05.11 |
운영체제 - 캐시(Cache)이론 (0) | 2023.05.02 |
운영체제 - 쓰레드(Thread), 기아상태,쓰레드풀(Thread Pool) (0) | 2023.04.28 |
댓글