📄 AutoResetEvent
SpinLock에서 스레드는 언락 된 상태를 확인할 때까지 계속 반복하여 체크를 해야 했습니다. 하지만 이것은 자원을 낭비하는 것으로 적합하지 못할 수 있습니다. C#에서 제공하는 AutoResetEvent는 while(true){}처럼 무한루프를 돌지 않고, 커널에 요청을 하여 "내가 접근할 수 있을 때 알려줘"라는 요청을 할 수 있습니다. 이것은 매우 간편하게 사용할 수 있지만, 커널에 접근하여 수행을 하기에 매우 느리다는 단점이 있습니다.
📑 AutoResetEvent
namespace ServerCore
{
class Lock
{
//boolean 변수 같은 개념
//Auto: 자동으로 문을 닫아주는 기능을 포함
AutoResetEvent _available = new AutoResetEvent(true); //초기 상태를 설정하며 생성
//ManualResetEvent _available = new ManualResetEvent(true);
public void Acquire()
{
_available.WaitOne(); //입장을 시도
//_available.Reset(); //잠그기, Auto는 WaitOne에서 자동으로 잠궈준다.
}
public void Release()
{
_available.Set();
}
}
class Program
{
static int number = 0;
static Lock lock = new Lock();
static void Thread1()
{
for(int i = 0; i < 10000; ++i)
{
lock.Acquire();
number++;
lock.Release();
}
}
static void Thread2()
{
for (int i = 0; i < 10000; ++i)
{
lock.Acquire();
number--;
lock.Release();
}
}
static void Main(string[] args)
{
Task task1 = new Task(Thread1);
Task task2 = new Task(Thread2);
task1.Start();
task2.Start();
Task.WaitAll(task1, task2);
//예상되는 출력은 0
Console.WriteLine(number);
}
}
}
- AutoResetEvent를 사용한 락 구현입니다.
- 매우 편리하고, 직관적으로 사용할 수 있는 장점이 있지만 편리한만큼 성능은 매우 느립니다.
📑 ManualResetEvent
namespace ServerCore
{
class Lock
{
//boolean 변수 같은 개념, Reset을 호출하여 수동으로 문을 닫아줘야함
ManualResetEvent _available = new ManualResetEvent(true);
public void Acquire()
{
_available.WaitOne(); //입장을 시도
_available.Reset(); //문을 닫는다, 하지만 원자성이 없는 호출
}
public void Release()
{
_available.Set();
}
}
class Program
{
static int number = 0;
static Lock lock = new Lock();
static void Thread1()
{
for(int i = 0; i < 10000; ++i)
{
lock.Acquire();
number++;
lock.Release();
}
}
static void Thread2()
{
for (int i = 0; i < 10000; ++i)
{
lock.Acquire();
number--;
lock.Release();
}
}
static void Main(string[] args)
{
Task task1 = new Task(Thread1);
Task task2 = new Task(Thread2);
task1.Start();
task2.Start();
Task.WaitAll(task1, task2);
//예상되는 출력은 0
Console.WriteLine(number);
}
}
}
- ManualResetEvent를 사용하여 구현하였습니다.
- AutoResetEvent와 다르게 Reset을 자동으로 해주지 않아 개발자가 직접 호출해야 합니다.
- 하지만 직접 호출하는 방식이라 원자성이 보장되지 않은 호출이기에 결괏값이 0이 나오지 않았습니다.
- 한번 열리면 대기 중이던 모든 스레드를 실행하게 합니다.
- 수동으로 Reset()을 호출하여 문을 닫고 이후 도착한 스레드들을 다시 대기하도록 합니다.
'server > socket server' 카테고리의 다른 글
[C# 서버] ReaderWriterLock (0) | 2023.01.10 |
---|---|
[C# 서버] Mutex (0) | 2023.01.09 |
[C# 서버] Context Switching (0) | 2023.01.04 |
[C# 서버] 스핀락 (0) | 2023.01.04 |
[C# 서버] 원자성, Interlocked, Monitor, lock (0) | 2023.01.02 |