이번 글에서는 Context Switching(컨텍스트 전환)이라는 개념에대해 배우고 Lock을 구현하면서 Context Switching이 발생하는 이유에대해서 알아보자
Context Switching 이란 운영 체제가 한 스레드(또는 프로세스)의 실행 상태를 저장하고, 다른 스레드(또는 프로세스)로 전환할 때 발생하는 작업과 그로 인한 비용을 말한다.
이번에 구현할 방식은 누군가 lock을 점유하고있으면 일단 스레드를 반환하고 랜덤하게 다시 접근을 시도하는 방식이다.
전 글의 SpinLock구현과 매우 비슷한데, 차이점은 점유에 실패했으면 어떻게 행동할것인가에 차이를 주기만 하면된다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ServerCore
{
class SpinLock
{
volatile int _locked = 0;
public void Aquire()
{
while (true)
{
//CAS Compare and Swap
int expected = 0;
int desire = 1;
if(Interlocked.CompareExchange(ref _locked, desire, expected) == expected)
break;
//Thread.Sleep(1);
//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 < 100000; i++)
{
_lock.Aquire();
num++;
_lock.Release();
}
}
static void Thread_2()
{
for (int i = 0; i < 100000; i++)
{
_lock.Aquire();
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(0), Thread.Sleep(1) , Thread.Yield()중에서 하나 골라서 쓰면된다.
셋다 동일하게 작동은하나 뉘앙스는 좀 다르다.
Thread.Sleep(1) : 무조건 1ms 휴식 => 1ms은 희망사항이고 실제로 몇 초 쉴지는 운영체제가 스케쥴러로 정함,최대한 요청한거 맞추려고는한다.
Thread.Sleep(0) : 조건부 양보 => 나보다 우선순위가 같거나 높은 스레드가 있으면 양보, 없으면 다시 돌아옴
Thread.Yield() : 관대한 양보 => 지금 실행이 가능한 스레드가 있으면 양보함 => 실행가능한 스레드가없으면 남은 시간 소진
누군가 lock을 점유하고있을때 일단 스레드를 반환하고 랜덤하게 다시 접근을 시도하는 방식이라는것이 spinLock과의 차이이다.
Context Switching의 과정을 살펴보면 단순 스레드만 옮기는 작업이아니라 커널모드로 들어가서 어떤 스레드를 사용할지를 선택하고, 사용할 스레드의 정보를 restore해주는 작업이 필요하므로 이것또한 자원이 만만치 않게 소비된다.
그러므로 상황에 맞춰서 선택적으로 하는것이 중요하다.
'서버 공부' 카테고리의 다른 글
| [서버 공부]8.ReaderWriterLock 개념 + 구현연습 (0) | 2025.01.07 |
|---|---|
| [서버 공부]7.AutoResetEvent (0) | 2025.01.07 |
| [서버 공부]5.Lock구현 - SpinLock (0) | 2025.01.02 |
| [서버 공부]4. Lock (0) | 2025.01.02 |
| [서버 공부]3.메모리 배리어 + Interlocked (0) | 2025.01.01 |