AutoResetEvent를 이용한 Lock구현 방식은 전에 소개했던 spinlock과 반환후 랜덤으로 접근하는방식과의 차이점으로 AutoResetEvent는 운영체제에서 커널 객체로 관리된다는 점이다.따라서 커널수준에서 동기화 작업이 일어난다.
이 방식은 누군가 이미 점유하고있는상태면 일단 반환하고 작업이 가능할때 AutoResetEvent이 알림을줘 다시 작업할수있게 해주는 방식이다.
AutoResetEvent는 일종의 자동문이라고 생각하면 편한게, 한 스레드가 들어오면 자동으로 잠궈서 다른 스레드가 못들어오게 막는다. 즉 WaitOne함수에는 입장시도를하고 성공시 잠궈주는 역할도 원자적으로 수행한다.
반복수를 1000회로 매우 낮췄는데 전 글은 100000번도 가능했지만 contextSwitching비용이 너무 커서 이걸로 구현하면 바로 출력되지않고 시간이 지나서 출력되기때문에 확연한 성능차이를 볼 수 있다.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ServerCore
{
class Lock
{
volatile int _locked = 0;
AutoResetEvent _available = new AutoResetEvent(true);
public void Aquire()
{
_available.WaitOne();//입장시도
//자동으로 닫아줌
}
public void Release()
{
_available.Set(); //다시 열어줌
}
}
internal class Program
{
static int num = 0;
static Lock _lock = new Lock();
static void Thread_1()
{
for (int i = 0; i < 1000; i++)
{
_lock.Aquire();
num++;
_lock.Release();
}
}
static void Thread_2()
{
for (int i = 0; i < 1000; 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);
}
}
}
이것외에도ManualResetEvent라는것도 존재하는데 이것은 수동으로 문을 닫아주는 역할이다.
ManualResetEvent _available = new ManualResetEvent(true);
public void Aquire()
{
_available.WaitOne();//입장시도
_available.Reset();
}
public void Release()
{
_available.Set();
위 코드를 이런식으로해서 .Reset을통해 수동으로 닫아준다. 하지만 지금까지의 경험을토대로 이렇게하면 당연히 원자적으로 작업이 일어나지 않기때문에 값이 제대로 계산이 안된다.
그래서 Manual은 이런 lock구현보다는 다른 용도로 쓰인다.
다음으로 Mutex이다. 이것도 커널동기화 객체라서 똑같이 느리고하는일도 거의 똑같은데, 차이점은 Event보다 더 많은 정보를 가지고있다는 점이다.에를들어 점유한 Thread id를 가지고있어 lock해제를 다른 Tread가 시도하면 release를 안해주는 것도 구현가능하다.
'서버 공부' 카테고리의 다른 글
| [서버 공부]9. 네트워크 기초 + 통신모델 + 소켓프로그래밍 입문 (0) | 2025.01.09 |
|---|---|
| [서버 공부]8.ReaderWriterLock 개념 + 구현연습 (0) | 2025.01.07 |
| [서버 공부]6.Lock구현 - Context Switching (0) | 2025.01.03 |
| [서버 공부]5.Lock구현 - SpinLock (0) | 2025.01.02 |
| [서버 공부]4. Lock (0) | 2025.01.02 |