락(Lock)과 데드락(DeadLock)
이번 포스팅은 락Lock과 데드락DeadLock에 관하여 알아보겠습니다. 먼저 간단히 비유를 통해 이해해보겠습니다.
| Lock | 들어와서 문을 잠가주고 나갈때 다시 열어놓는 행위. |
|---|---|
| DeadLock | 나갈때 문을 안열어놓고 가서 다음 들어올 사람이 못들어오는 현상.(창문으로 몰래 나감) |
Lock은 이미 우리가 지난 포스팅(경합조건과 원자성)에서 실습을 해보았습니다. 쪼개어지지 않도록 원자성이 확보된 코드 뭉치를 원할 때 Interlocked.Increment() 메소드를 사용했었습니다. 이번에는 Increment(), Decrement()외에 어떠한 내용의 코드라도 원자성을 확보하게 해주는 방법을 알아보겠습니다. 예시는 지난 포스팅에서와 같이 number++, number– 쓰레드 예시입니다.
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
using System;
using System.Data;
using System.Threading;
namespace ServerCore
{
internal class Program
{
static int number = 0;
static object _obj = new object();
static void Thread_1()
{
for (int i = 0; i < 1000000; i++)
{
number++;
}
}
static void Thread_2()
{
for (int i = 0; i < 1000000; i++)
{
number--;
}
}
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(number);
}
}
}
분명 똑같은 횟수만큼 더하고 빼줬는데 뺀 횟수가 더 많은 것처럼 출력이 되었습니다. 이제 코드의 원자성을 확보해줍시다. 사용할 방법은 두 가지입니다. 하나는 Monitor메소드를 사용하는 방법,
1
2
3
4
static object _obj = new object();
Monitor.Enter(_obj);
// 원하는 코드 뭉치
Monitor.Exit(_obj);
다른 하나는 lock(){ }를 사용하는 방법입니다.
1
2
3
4
5
static object _obj = new object();
lock (_obj)
{
// 원하는 코드 뭉치
}
각각 사용해봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static object _obj = new object();
static void Thread_1()
{
for (int i = 0; i < 1000000; i++)
{
// 상호배제 Mutual Exclusive
Monitor.Enter(_obj); //화장실 들어와서 문을 잠그는 행위
number++;
Monitor.Exit(_obj); //잠금을 풀고 화장실에서 나간다.
}
}
static void Thread_2()
{
for (int i = 0; i < 1000000; i++)
{
Monitor.Enter(_obj);
number--;
Monitor.Exit(_obj);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static object _obj = new object();
static void Thread_1()
{
for (int i = 0; i < 1000000; i++)
{
// 상호배제 Mutual Exclusive
lock (_obj)
{
number++;
}
}
}
static void Thread_2()
{
for (int i = 0; i < 1000000; i++)
{
lock (_obj)
{
number--;
}
}
}
두 쓰레드 연산이 짝이 맞추어져 잘 실행되었습니다.
그럼 이제 데드락DeadLock을 일으켜봅시다. Monitor.Exit() 만 빼주면 됩니다.
누군가 방에 들어가기는 했는데 나오질 않아서 다음 순번의 사람이 못들어가고 하염없이 기다리고만 있는 상황입니다. 실수로 Exit()을 빼주거나, 코드 상에서 Exit()의 수행이 안되도록 로직을 짠다면 데드락이 쉽게 일어날 수 있습니다. 그래서 데드락을 방지하기 위해서는 더 사용이 간편한 lock()을 사용하는 게 좋겠습니다.
| Monitor() | .Enter()와 .Exit()으로 들어오고 나감.만약 .Exit()을 빼먹고 실행하지 못한다면 다음 순번이 .Enter()를 못하게 되는 위험(데드락)이 있음. |
|---|---|
| lock() | 중괄호{ }로 열고 닫아주므로 데드락을 일으키는 실수를 줄일 수 있음. |
이 글은 인프런의 '[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버' 강의를 참고하여 정리 목적으로 작성하였습니다. 감사합니다.



