Context Switching 구현하기 with SpinLock
지난 포스팅에서는 SpinLock에 관하여 구현해보았습니다. 바로 Interlocked.CompareExchange() 메소드를 사용했었죠. 이번 포스팅에서는 컨텍스트 스위칭Context Switching에 관하여 알아보고 구현해보겠습니다. 컨텍스트 스위칭은 앞서 멀티쓰레드 프로그래밍을 소개하는 첫 번째 글에서 잠깐 언급이 되었었는데요, 이번에 좀 더 자세하게 알아봅시다.
프로세스Process란, 실행파일을 클릭했을 때, 코드가 메모리공간으로 올라가서 메모리(RAM)를 할당받게 된 이후의 프로그램을 뜻합니다.
컨텍스트 스위칭Context Switching이란, 여러 개의 프로세스가 실행되고 있을 때 실행되던 프로세스를 중단하고 다른 프로세스를 실행하는 기술입니다. 구현되는 원리는 PCB(Process Context/Control Block)이라고 하는 메모리의 별도 공간에 process 상태값들을 저장하고, 해당 값들을 불러오는 원리입니다.
프로세스가 실행하면 메모리(RAM)에 해당 프로세스의 정보를 생성하고 종료 시 삭제합니다. 여기서 PCB(Process Context/Control Block)에는 프로세스의 컨택스트와 실행 흐름제어 정보를 저장합니다.
멀티쓰레드로 프로세스를 실행하면 각 쓰레드만을 위한 독립적인 쓰레드스택ThreadStack 메모리 영역이 생성됩니다. 그리고 쓰레드스택에서는 실행 흐름을 위한 최소 정보만을 보관하고, 나머지 영역(Code, Data, Heap)은 모든 쓰레드가 부모 프로세스의 영역을 함께 공유합니다.
예를 들어 3가지 일을 수행하기 위해서 싱글쓰레드 멀티프로세스를 여러개 실행하는 것이 아래 왼쪽 그림, 멀티쓰레드 프로세스를 한개 실행하는 것이 아래 오른쪽 그림과 같습니다. 멀티쓰레드 프로그래밍을 할 경우, 공유 영역이 있기 때문에 컨택스트 스위칭이 효율적이고 빠르게 일어날 수 있는 장점이 있습니다.
그럼 이제 c#에서 멀티쓰레드의 Context Switching을 간단히 구현해보겠습니다. 지난 포스팅에서 만들었던 예시를 그대로 사용하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using System;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
namespace ServerCore
{
class SpinLock
{
volatile int _locked = 0;
public void Acquire()
{
while (true)
{
// CAS Compare-And-Swap
int expected = 0;
int desired = 1;
if (Interlocked.CompareExchange(ref _locked, desired, expected) == 0)
break;
// 쉬다 올게~
Thread.Sleep(1); // 무조건 휴식 => 무조건 1ms 정도 쉬고 싶어요
Thread.Sleep(0); // 조건부 양보 => 나보다 우선순위가 낮은 애들한테는 양보 불가 => 우선순위가 나보다 같거나 높은 쓰레드가 없으면 다시 본인한테
Thread.Yield(); // 관대한 양보 => 관대하게 양보할테니, 지금 실행이 가능한 쓰레드가 있으면 실행하세요 => 실행 가능한 애가 없으면 남은 시간 소진.
}
}
public void Release()
{
_locked = 0;
}
}
internal class Program
{
static int _num = 0;
static SpinLock _lock = new SpinLock();
static void Thread_1()
{
for (int i = 0; i < 1000000; i++)
{
_lock.Acquire();
_num++;
_lock.Release();
}
}
static void Thread_2()
{
for (int i = 0; i < 1000000; i++)
{
_lock.Acquire();
_num--;
_lock.Release();
}
}
static void Main(string[] args)
{
Task t1 = new Task(Thread_1);
Task t2 = new Task(Thread_2);
t1.Start();
t2.Start();
Task.WaitAll(t1, t2);
Console.WriteLine(_num);
}
}
}
컨텍스트 스위칭을 위해 추가된 부분은 Thread.Sleep()과 Thread.Yield() 입니다. 이렇게 간단히 테스트해볼 수 있었습니다. 감사합니다:)
이 글은 인프런의 '[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버' 강의를 참고하여 정리 목적으로 작성하였습니다. 감사합니다.
https://applefarm.tistory.com/105 https://hyunie-y.tistory.com/31 https://agh2o.tistory.com/12 https://goodgid.github.io/What-is-Multi-Thread/


