자바 다 중 스 레 드 에서 자원 경쟁 을 해결 하 는 7 가지 방법 에 대한 상세 한 설명

머리말
일반적인 상황 에서 다 중 스 레 드 프로 그래 밍 과 관련 되면 프로그램의 복잡성 이 현저히 상승 하고 성능 이 현저히 떨 어 지 며 BUG 가 나타 날 확률 이 크게 높아진다.
다 중 스 레 드 프로 그래 밍 은 프로그램 을 병행 하여 실행 하고 데이터 처리 능력 을 향상 시 키 는 것 이 목적 이지 만 대부분 상황 에서 공유 자원 의 경쟁 과 관련 되 기 때문에 자원 을 수정 하 는 것 이다.
대상 시 잠 금 처 리 를 해 야 합 니 다.그러나 자물쇠 의 실현 에는 여러 가지 방법 이 있 습 니 다.다음은 C\#언어 에서 몇 가지 자물쇠 의 실현 과 그 성능 표현 을 알 아 보 겠 습 니 다.
1.c\#아래 의 몇 가지 자물쇠 활용 방식
1.임계 구역 은 다 중 스 레 드 에 대한 직렬 화 를 통 해 공공 자원 이나 코드 를 방문 하고 속도 가 빠 르 며 데이터 접근 을 제어 하기에 적합 합 니 다.

private static object obj = new object();
  private static int lockInt;
  private static void LockIntAdd()
  {
   for (var i = 0; i < runTimes; i++)
   {
    lock (obj)
    {
     lockInt++;
    }
   }
  }
c\#의 lock 문법 은 임계 구역(Monitor)의 문법 사탕 입 니 다.이것 은 대략 90%이상 의.net 프로그래머 가 먼저 생각 하 는 자물쇠 입 니 다.그러나 대부분 사람들 은 알 고 있 을 뿐 입 니 다.
이런 문법 이 있 는데 사실은 임계 구역 의 방식 으로 자원 경쟁 을 처리 하 는 것 인지 모르겠다.
2.상호 배척 량 은 공유 자원 에 대한 공동 방문 을 조율 하기 위해 디자인 된 것 이다.
c\#에 Mutex 류 가 있 습 니 다.System.Threading 네 임 스페이스 에서 Mutex 는 바로 상호 배척 량 입 니 다.상호 배척 량 은 다 중 스 레 드 간 의 자원 경쟁 만 처리 할 수 있 는 것 이 아니 라 처리 할 수 있 습 니 다.
프로 세 스 간 의 자원 경쟁 은 기능 이 비교적 강하 지만 비용 도 많 고 성능 도 비교적 낮다.

private static Mutex mutex = new Mutex();
  private static int mutexInt;
  private static void MutexIntAdd()
  {
   for (var i = 0; i < runTimes; i++)
   {
    mutex.WaitOne();
    mutexInt++;
    mutex.ReleaseMutex();
   }
  }
3.신 호 량 은 유한 한 수량 을 가 진 사용자 자원 을 제어 하기 위해 설계 되 었 다.

private static Semaphore sema = new Semaphore(1, 1);
  private static int semaphoreInt;
  private static void SemaphoreIntAdd()
  {
   for (var i = 0; i < runTimes; i++)
   {
    sema.WaitOne();
    semaphoreInt++;
    sema.Release();
   }
  }
4.이벤트:스 레 드 에 일부 사건 이 발생 했 음 을 알 리 고 후속 작업 의 시작 을 시작 합 니 다.

public static AutoResetEvent autoResetEvent = new AutoResetEvent(true);
  private static int autoResetEventInt;
  private static void AutoResetEventIntAdd()
  {
   for (var i = 0; i < runTimes; i++)
   {
    if (autoResetEvent.WaitOne())
    {
     autoResetEventInt++;
     autoResetEvent.Set();
    }
   }
  }
5.읽 기와 쓰기 자물쇠,이 자 물 쇠 는 다른 프로그램 이 쓰 고 있 는 상황 에서 자원 을 읽 을 수 있 기 때문에 자원 이 더러 운 읽 기 를 허용 한다 면 이 걸 사용 하 는 것 이 좋 습 니 다.

private static ReaderWriterLockSlim LockSlim = new ReaderWriterLockSlim();
  private static int lockSlimInt;
  private static void LockSlimIntAdd()
  {
   for (var i = 0; i < runTimes; i++)
   {
    LockSlim.EnterWriteLock();
    lockSlimInt++;
    LockSlim.ExitWriteLock();
   }
  }
6.원자 잠 금,원자 조작 Interlocked.CompareExchange 를 통 해"잠 금 없 음"경쟁 실현

private static int isLock;
  private static int ceInt;
  private static void CEIntAdd()
  {
   //long tmp = 0;
   for (var i = 0; i < runTimes; i++)
   {
    while (Interlocked.CompareExchange(ref isLock, 1, 0) == 1) { Thread.Sleep(1); }

    ceInt++;
    Interlocked.Exchange(ref isLock, 0);
   }
  }
7.원자 성 조작 은 일종 의 특례 로 야외 원자 성 조작 자체 가 타고 난 라인 이 안전 하기 때문에 자 물 쇠 를 채 울 필요 가 없다.

private static int atomicInt;
  private static void AtomicIntAdd()
  {
   for (var i = 0; i < runTimes; i++)
   {
    Interlocked.Increment(ref atomicInt);
   }
  }
8.자 물 쇠 를 넣 지 않 고 자 물 쇠 를 넣 지 않 으 면 다 중 스 레 드 에서 실 행 된 결과 가 틀 렸 을 것 입 니 다.여기에 붙 여 성능 을 비교 해 보 겠 습 니 다.

private static int noLockInt;
  private static void NoLockIntAdd()
  {
   for (var i = 0; i < runTimes; i++)
   {
    noLockInt++;
   }
  }
2.성능 테스트
1.테스트 코드,실행 1000,10000,100000,1000000 회

private static void Run()
  {
   var stopwatch = new Stopwatch();
   var taskList = new Task[loopTimes];

   //    
   Console.WriteLine();
   Console.WriteLine($"       :{loopTimes}");
   Console.WriteLine($"       :{runTimes}");
   Console.WriteLine($"        :{runTimes * loopTimes}");

   // AtomicIntAdd
   stopwatch.Restart();
   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { AtomicIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("AtomicIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{atomicInt}");

   // CEIntAdd
   taskList = new Task[loopTimes];
   stopwatch.Restart();

   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { CEIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("CEIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{ceInt}");

   // LockIntAdd
   taskList = new Task[loopTimes];
   stopwatch.Restart();

   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { LockIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("LockIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{lockInt}");

   // MutexIntAdd
   taskList = new Task[loopTimes];
   stopwatch.Restart();

   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { MutexIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("MutexIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{mutexInt}");

   // LockSlimIntAdd
   taskList = new Task[loopTimes];
   stopwatch.Restart();

   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { LockSlimIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("LockSlimIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{lockSlimInt}");

   // SemaphoreIntAdd
   taskList = new Task[loopTimes];
   stopwatch.Restart();

   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { SemaphoreIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("SemaphoreIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{semaphoreInt}");


   // AutoResetEventIntAdd
   taskList = new Task[loopTimes];
   stopwatch.Restart();

   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { AutoResetEventIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("AutoResetEventIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{autoResetEventInt}");

   // NoLockIntAdd
   taskList = new Task[loopTimes];
   stopwatch.Restart();

   for (var i = 0; i < loopTimes; i++)
   {
    taskList[i] = Task.Factory.StartNew(() => { NoLockIntAdd(); });
   }
   Task.WaitAll(taskList);
   Console.WriteLine($"{GetFormat("NoLockIntAdd")},    :{stopwatch.ElapsedMilliseconds}  ,    :{noLockInt}");
   Console.WriteLine();
  }
2.라인:10




3.라인:50


3.총화


1)각종 테스트 에서 자 물 쇠 를 채 우지 않 는 것 이 가장 빠 를 것 이 므 로 자원 경쟁 으로 인해 자 물 쇠 를 채 워 운행 하 는 것 을 피한다.
2)다 중 스 레 드 에서 Interlocked.CompareExchange 는 항상 우수한 성능 을 보 여 2 위 를 차지 했다.
3)세 번 째 lock,임계 구역 도 좋 은 성능 을 보 였 으 니 다른 사람 이 lock 성능 이 낮다 고 말 할 때 반박 하 세 요.
4)네 번 째 는 원자 변수(Atomic)작업 이지 만 현 재 는 변수의 자체 증가 와 감소 만 지원 하고 적용 성 이 강하 지 않다.
5)다섯 번 째 읽 기와 쓰기 자물쇠(Reader Writer LockSlim)의 표현 도 괜 찮 고 읽 을 것 이 없 는 것 을 지원 하 며 실용성 이 좋 습 니 다.
6)남 은 신 호 량,사건,상호 배척 량 등 세 가지 성능 이 가장 떨어진다.물론 그들 은 각자 의 적용 범 위 를 가지 고 있 지만 자원 경쟁 을 처리 하 는 데 있어 서 좋 지 않다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기