C\#읽 기 및 쓰기 잠 금 Reader WriterLockSlim 을 어떻게 사용 합 니까?

읽 기와 쓰기 자물쇠 의 개념 은 매우 간단 하 다.여러 스 레 드 가 동시에 읽 기 자 물 쇠 를 가 져 올 수 있 지만 같은 시간 에 한 스 레 드 만 쓰기 자 물 쇠 를 가 져 올 수 있 기 때문에 공유-독점 자물쇠 라 고도 부른다.C\#에 서 는 Reader Writer LockSlim 류 를 사용 하여 읽 기와 쓰기 잠 금 기능 을 완성 하 는 것 을 추천 합 니 다.
어떤 경우 에 한 대상 에 대한 읽 기 횟수 는 수정 횟수 보다 훨씬 많 고 간단 한 lock 방식 으로 자 물 쇠 를 추가 하면 읽 기 효율 에 영향 을 줄 수 있 습 니 다.읽 기와 쓰기 자 물 쇠 를 사용 하면 여러 스 레 드 가 이 대상 을 동시에 읽 을 수 있 고 대상 이 기록 되 어 자 물 쇠 를 차지 할 때 만 막 을 수 있 습 니 다.
쉽게 말 하면 특정한 스 레 드 가 읽 기 모드 에 들 어 갈 때 다른 스 레 드 는 읽 기 모드 에 들 어 갈 수 있 습 니 다.이 스 레 드 가 기록 모드 에 들 어가 야 한다 고 가정 하면 그 는 막 힐 수 밖 에 없습니다.읽 기 모드 가 종 료 될 때 까지
마찬가지 로 어떤 스 레 드 가 기록 모드 에 들 어가 면 다른 스 레 드 는 기록 하 든 읽 든 막 힐 수 있 습 니 다.
쓰기/읽 기 모드 에 들 어 가 는 방법 은 두 가지 가 있 습 니 다.
EnterReadLock 은 쓰기 모드 잠 금 상태 로 들 어 가 려 고 시도 합 니 다.
TryEnterReadLock(Int 32)은 읽 기 모드 잠 금 상태 에 들 어 가 려 고 시도 합 니 다.정수 시간 초 과 를 선택 할 수 있 습 니 다.
EnterWriteLock 은 쓰기 모드 잠 금 상태 로 들 어 가 려 고 시도 합 니 다.
TryEnterWriteLock(Int 32)은 기록 모드 잠 금 상태 에 들 어가 시간 초과 선택 을 시도 합 니 다.
쓰기/읽 기 모드 를 종료 하 는 방법 은 두 가지 가 있 습 니 다.
ExitReadLock 은 읽 기 모드 의 재 귀 계 수 를 줄 이 고 생 성 된 계수 가 0(0)일 때 읽 기 모드 를 종료 합 니 다.
ExitWriteLock 은 쓰기 모드 의 재 귀 계 수 를 줄 이 고 생 성 된 계수 가 0(0)일 때 쓰기 모드 를 종료 합 니 다.
다음은 사용법 을 보 여 드 리 겠 습 니 다.

public class Program
  {
    static private ReaderWriterLockSlim rwl = new ReaderWriterLockSlim();
    static void Main(string[] args)
    {
      Thread t_read1 = new Thread(new ThreadStart(ReadSomething));
      t_read1.Start();
      Console.WriteLine("{0} Create Thread ID {1} , Start ReadSomething", DateTime.Now.ToString("hh:mm:ss fff"), t_read1.GetHashCode());
      Thread t_read2 = new Thread(new ThreadStart(ReadSomething));
      t_read2.Start();
      Console.WriteLine("{0} Create Thread ID {1} , Start ReadSomething", DateTime.Now.ToString("hh:mm:ss fff"), t_read2.GetHashCode());
      Thread t_write1 = new Thread(new ThreadStart(WriteSomething));
      t_write1.Start();
      Console.WriteLine("{0} Create Thread ID {1} , Start WriteSomething", DateTime.Now.ToString("hh:mm:ss fff"), t_write1.GetHashCode());
    }
    static public void ReadSomething()
    {
      Console.WriteLine("{0} Thread ID {1} Begin EnterReadLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      rwl.EnterReadLock();
      try
      {
        Console.WriteLine("{0} Thread ID {1} reading sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        Thread.Sleep(5000);//      
        Console.WriteLine("{0} Thread ID {1} reading end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
      finally
      {
        rwl.ExitReadLock();
        Console.WriteLine("{0} Thread ID {1} ExitReadLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
    }
    static public void WriteSomething()
    {
      Console.WriteLine("{0} Thread ID {1} Begin EnterWriteLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      rwl.EnterWriteLock();
      try
      {
        Console.WriteLine("{0} Thread ID {1} writing sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        Thread.Sleep(10000);//      
        Console.WriteLine("{0} Thread ID {1} writing end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
      finally
      {
        rwl.ExitWriteLock();
        Console.WriteLine("{0} Thread ID {1} ExitWriteLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
    }
  }


3 번 스 레 드 와 4 번 스 레 드 가 동시에 읽 기 모드 에 들 어 갈 수 있 고 5 번 스 레 드 가 5 초 지나 면(즉 3,4 번 스 레 드 가 읽 기 자 물 쇠 를 종료 한 후)쓰기 모드 에 들 어 갈 수 있 습 니 다.
상기 코드 를 수정 하고 먼저 쓰기 모드 의 스 레 드 2 개 를 연 다음 에 읽 기 모드 스 레 드 를 엽 니 다.코드 는 다음 과 같 습 니 다.

 static void Main(string[] args)
    {
      Thread t_write1 = new Thread(new ThreadStart(WriteSomething));
      t_write1.Start();
      Console.WriteLine("{0} Create Thread ID {1} , Start WriteSomething", DateTime.Now.ToString("hh:mm:ss fff"), t_write1.GetHashCode());
      Thread t_write2 = new Thread(new ThreadStart(WriteSomething));
      t_write2.Start();
      Console.WriteLine("{0} Create Thread ID {1} , Start WriteSomething", DateTime.Now.ToString("hh:mm:ss fff"), t_write2.GetHashCode());
      Thread t_read1 = new Thread(new ThreadStart(ReadSomething));
      t_read1.Start();
      Console.WriteLine("{0} Create Thread ID {1} , Start ReadSomething", DateTime.Now.ToString("hh:mm:ss fff"), t_read1.GetHashCode());
      Thread t_read2 = new Thread(new ThreadStart(ReadSomething));
      t_read2.Start();
      Console.WriteLine("{0} Create Thread ID {1} , Start ReadSomething", DateTime.Now.ToString("hh:mm:ss fff"), t_read2.GetHashCode());
    }
결 과 는 다음 과 같다.

3 번 스 레 드 와 4 번 스 레 드 는 모두 쓰기 모드 에 들 어가 야 하지만 3 번 스 레 드 는 쓰기 자 물 쇠 를 먼저 차지 하기 때문에 4 번 스 레 드 는 10s 를 기 다 려 야 들 어 갈 수 밖 에 없다.5 번 스 레 드 와 6 번 스 레 드 는 읽 기 자 물 쇠 를 사용 해 야 하기 때문에 4 번 스 레 드 가 종료 되 어야 자 물 쇠 를 기록 한 후에 야 계속 할 수 있 습 니 다.
TryEnterReadLock 과 TryEnterWriteLock 은 시간 초과 시간 을 설정 할 수 있 습 니 다.이 말 이 실 행 될 때 스 레 드 가 여기 서 막 힙 니 다.이때 자 물 쇠 를 사용 할 수 있다 면 true 로 돌아 갑 니 다.시간 초과 시간 까지 자 물 쇠 를 사용 하지 않 으 면 false 로 돌아 가 자물쇠 의 점용 을 포기 하고 아래 코드 를 계속 실행 합 니 다.
EnterUpgradeableReadLock
Reader WriterLockSlim 클래스 는 읽 기 모드 를 업그레이드 할 수 있 습 니 다.이 방식 과 읽 기 모드 의 차 이 는 EnterWriteLock 이나 TryEnterWriteLock 방법 을 호출 하여 쓰기 모드 로 업그레이드 하 는 것 입 니 다.매번 하나의 스 레 드 만 업그레이드 가능 모드 에 있 기 때문이다.업그레이드 가능 모드 의 스 레 드 에 들 어가 면 읽 기 모드 의 스 레 드 에 영향 을 주지 않 습 니 다.즉,하나의 스 레 드 가 업그레이드 가능 모드 에 들 어가 면 임의의 스 레 드 가 동시에 읽 기 모드 에 들 어가 서 막 히 지 않 습 니 다.기록 자 물 쇠 를 가 져 오 기 를 기다 리 고 있 는 여러 스 레 드 가 있다 면 EnterUpgradeableReadLock 을 실행 하면 스 레 드 가 시간 이 초과 되 거나 기록 자 물 쇠 를 종료 할 때 까지 막 힐 것 입 니 다.
다음 코드 는 읽 기 모드 에서 쓰기 자물쇠 로 업그레이드 하 는 방법 을 보 여 줍 니 다.

static public void UpgradeableRead()
    {
      Console.WriteLine("{0} Thread ID {1} Begin EnterUpgradeableReadLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      rwl.EnterUpgradeableReadLock();
      try
      {
        Console.WriteLine("{0} Thread ID {1} doing sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        Console.WriteLine("{0} Thread ID {1} Begin EnterWriteLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        rwl.EnterWriteLock();
        try
        {
          Console.WriteLine("{0} Thread ID {1} writing sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
          Thread.Sleep(10000);//      
          Console.WriteLine("{0} Thread ID {1} writing end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        }
        finally
        {
          rwl.ExitWriteLock();
          Console.WriteLine("{0} Thread ID {1} ExitWriteLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        }
        Thread.Sleep(10000);//      
        Console.WriteLine("{0} Thread ID {1} doing end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
      finally
      {
        rwl.ExitUpgradeableReadLock();
        Console.WriteLine("{0} Thread ID {1} ExitUpgradeableReadLock...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
    }
읽 기와 쓰기 자물쇠 가 성능 에 미 치 는 영향 은 뚜렷 하 다.
다음 테스트 코드:

public class Program
  {
    static private ReaderWriterLockSlim rwl = new ReaderWriterLockSlim();
    static void Main(string[] args)
    {
      Stopwatch sw = new Stopwatch();
      sw.Start();
      List<Task> lstTask = new List<Task>();
      for (int i = 0; i < 500; i++)
      {
        if (i % 25 != 0)
        {
          var t = Task.Factory.StartNew(ReadSomething);
          lstTask.Add(t);
        }
        else
        {
          var t = Task.Factory.StartNew(WriteSomething);
          lstTask.Add(t);
        }
      }
      Task.WaitAll(lstTask.ToArray());
      sw.Stop();
      Console.WriteLine("  ReaderWriterLockSlim  ,  :" + sw.Elapsed);
      sw.Restart();
      lstTask = new List<Task>();
      for (int i = 0; i < 500; i++)
      {
        if (i % 25 != 0)
        {
          var t = Task.Factory.StartNew(ReadSomething_lock);
          lstTask.Add(t);
        }
        else
        {
          var t = Task.Factory.StartNew(WriteSomething_lock);
          lstTask.Add(t);
        }
      }
      Task.WaitAll(lstTask.ToArray());
      sw.Stop();
      Console.WriteLine("  lock  ,  :" + sw.Elapsed);
    }
    static private object _lock1 = new object();
    static public void ReadSomething_lock()
    {
      lock (_lock1)
      {
        //Console.WriteLine("{0} Thread ID {1} reading sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        Thread.Sleep(10);//      
        //Console.WriteLine("{0} Thread ID {1} reading end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
    }
    static public void WriteSomething_lock()
    {
      lock (_lock1)
      {
        //Console.WriteLine("{0} Thread ID {1} writing sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        Thread.Sleep(100);//      
        //Console.WriteLine("{0} Thread ID {1} writing end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
    }
    static public void ReadSomething()
    {
      rwl.EnterReadLock();
      try
      {
        //Console.WriteLine("{0} Thread ID {1} reading sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        Thread.Sleep(10);//      
        //Console.WriteLine("{0} Thread ID {1} reading end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
      finally
      {
        rwl.ExitReadLock();
      }
    }
    static public void WriteSomething()
    {
      rwl.EnterWriteLock();
      try
      {
        //Console.WriteLine("{0} Thread ID {1} writing sth...", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
        Thread.Sleep(100);//      
        //Console.WriteLine("{0} Thread ID {1} writing end.", DateTime.Now.ToString("hh:mm:ss fff"), Thread.CurrentThread.GetHashCode());
      }
      finally
      {
        rwl.ExitWriteLock();
      }
    }
  }
상기 코드 는 500 개의 Task 에 있 습 니 다.모든 Task 는 하나의 스 레 드 탱크 스 레 드 를 차지 합 니 다.그 중에서 20 개의 스 레 드 와 480 개의 읽 기 스 레 드 를 기록 하고 시 뮬 레이 션 을 합 니 다.그 중에서 데 이 터 를 읽 는 데 10ms 가 들 고 쓰기 동작 은 100 ms 가 들 며 각각 lock 방식 과 Reader Writer LockSlim 방식 을 테스트 했다.Reader Writer LockSlim 에 대해 480 개의 스 레 드 를 동시에 읽는다 고 가정 하면 10ms 를 소모 하고 20 개의 기록 작업 은 2000 ms 를 차지 하기 때문에 2010 ms 를 소모 합 니 다.일반적인 lock 방식 은 모두 독점 적 이기 때문에 480 개의 읽 기 작업 은 시간 간 4800 ms+20 개의 기록 작업 2000 ms=6800 ms 를 차지 합 니 다.운행 결 과 는 성능 향상 이 뚜렷 하 다 는 것 을 보 여 주 었 다.

이상 은 본 고의 모든 내용 입 니 다.읽 기와 쓰기 자 물 쇠 를 능숙 하 게 활용 하 는 데 도움 이 되 기 를 바 랍 니 다.

좋은 웹페이지 즐겨찾기