The ReaderWriterLock Class
음..이 클래스는 MSDN에 의하면
ReaderWriterLock을 사용하여 리소스에 대한 액세스를 동기화합니다. 지정된 시점에 여러 스레드에 대한 동시 읽기 액세스 또는 단일 스레드에 대한 쓰기 액세스를 허용합니다. 리소스가 자주 변경되는 경우 ReaderWriterLock을 사용하면 Monitor 같이 한 번에 하나씩 수행되는 단순 잠금을 사용할 때보다 효과적으로 처리할 수 있습니다.
ReaderWriterLock은 주로 읽기 작업을 위해 액세스가 수행되며 짧은 기간에 걸쳐 가끔 쓰기가 수행되는 경우에 가장 잘 작동합니다. 다중 판독기와 단일 작성기는 서로 번갈아 작동되므로 판독기와 작성기 모두 오래 동안 잠금 상태가 유지되지 않습니다.
이 클래스에는 주요한 4개의 메서드가 존재한다.
- AcquireReaderLock() - 이 메서드는 Reader의 잠금 상태를 가져오며 Timespan형과 int 형의 두가지의 timeout 시간의 매개변수를 갖는다. 이 timeout 매개변수는 데드락(deadlock)를 발견할수 있는 좋은 도구가 될수 있다.
- AcquireWriterLock() - 이 메서드는 Writer의 잠금 상태를 가져오며 위 메서드와 마찬가지로 Timespan형과 int형의 두가지의 timeout 매개변수를 갖는다.
- ReleaseReaderLock() - Reader의 잠금을 해제한다.
- ReleaseWriterLock() - Writer의 잠금을 해제한다.
ReaderWriterLock 클래스를 사용하게 되면 몇개의 스레드를 사용하든지 상관없이 스레드안전하게 데이터를 읽을수 있다.
Reader 스레드는 개체가 어떤 Writer스레드에도 묶여 있지 않을때 잠김을 가져 올수 있으며 Writer 스레드는 어떤 Writer 스레드나 Reader 스레드에 묶여 있지 않을때 잠김을 가져올 수 있다. !!
예제를 살펴 보자.
아래는 ReadWrite 라는 클래스의 전문이다.
10 private ReaderWriterLock rwl;
11 private int x;
12 private int y;
13
14 public ReadWrite()
15 {
16 this.rwl = new ReaderWriterLock();
17 }
18
19 public void ReadInts(ref int a, ref int b)
20 {
21 this.rwl.AcquireReaderLock(Timeout.Infinite);
22
23 try
24 {
25 a = this.x;
26 b = this.y;
27 }
28 finally
29 {
30 this.rwl.ReleaseReaderLock();
31 }
32 }
33
34 public void WriteInts(int a, int b)
35 {
36 this.rwl.AcquireWriterLock(Timeout.Infinite);
37
38 try
39 {
40 this.x = a;
41 this.y = b;
42 Console.WriteLine("x = " + this.x + " y = " + this.y + " Thread ID = " + Thread.CurrentThread.ManagedThreadId.ToString());
43 }
44 finally
45 {
46 this.rwl.ReleaseWriterLock();
47 }
48 }
11라인과 12라인의 int 형 필드가 스레드에서 테스트할 리소스이다.
ReadInts()메서드와 WriteInts()메서드는 이 int형 필드들을 읽고 쓰는 작업을 하는데 각각 잠김을 가져오며 해제하는 코드를 가지고 있다.
10 private ReadWrite rw = new ReadWrite();
11
12 private void Write()
13 {
14 int a = 10;
15 int b = 11;
16 Console.WriteLine("*********** Write **************");
17
18 for (int i = 1; i <= 5; i++)
19 {
20 this.rw.WriteInts(a++, b++);
21 Thread.Sleep(1000);
22 }
23 }
24
25 private void Read()
26 {
27 int a = 10;
28 int b = 11;
29 Console.WriteLine("*********** Read **************");
30
31 for (int i = 1; i <= 5; i++)
32 {
33 this.rw.ReadInts(ref a, ref b);
34 Console.WriteLine("For i= " + i + " a = " + a + " b = " + b + " Thread ID = " + Thread.CurrentThread.ManagedThreadId.ToString());
35 Thread.Sleep(1000);
36 }
37 }
38
39 static void Main(string[] args)
40 {
41 Program p = new Program();
42 Thread wt1 = new Thread(p.Write);
43 Thread wt2 = new Thread(p.Write);
44 wt1.Start();
45 wt2.Start();
46
47 Thread rt1 = new Thread(p.Read);
48 Thread rt2 = new Thread(p.Read);
49 rt1.Start();
50 rt2.Start();
51 }
위 클래스는 메인 클래스의 코드이다.
앞서 만든 ReadWrite클래스의 ReadInts()메서드와 WriteInts() 메서드를 호출하고자 하는 Read()메서드와 Write()메서드를 생성한다.
그리고 42라인에서 보여지는 것 처럼 각각 스레드를 만들어서 이 메서드들을 호출한다.
예제를 실행해보면 아래와 같은 결과가 나오게 된다.
이제 분석을 해보자..좀..많이 헤깔린다..ㅡ.ㅡ;
WriteInts()메서드에서 필드 x와 y는 a와 b의 값으로 대입된다. AcquireWriterLock()메서드가 호출되어 해당 스레드는 쓰기 잠금 상태가 되었기 때문에 ReleaseWriterLock()메서드로 해제시키기 전까지는 어떤 스레드(여기서는 rt1 읽기 스레드와 rt2 읽기 스레드)도 해당 개체에 액세스를 할 수 없게 된다. 이런 일련의 상황들은 Monitor와 비슷한다..^^;
ReadInts() 메서드도 살펴 보도록 하자.
스레드 rt1과 rt2가 AcquireReaderLock()메서드로 동시에 읽기 잠금을 가져오게 되면 역시 읽기 스레드인 wt1과 wt2는 해당 개체에 액세스를 할 수 없게 된다.
앞서 말했지만 reader 스레드만이 읽기잠금 후에 개체에 동시에 액세스가 가능하다.
Monitor의 경우에는 데이터를 읽는데 있어 최적의 안정성을 보장한다. 그러나 동시에 특정 리소스(데이터)를 여러개의 스레드가 읽고 쓰고 하는 경우에는 ReaderWriterLock 이 더더욱 우아하고 아름다운(??) 코드를 만들수 있다고 본다..쿨럭..
다음 강좌에는 Manual Synchronization에 대해서 알아 보도록 하겠다..