깊이 분석 C\#스 레 드 동기 화

전편 은 스 레 드 를 어떻게 열 고 스 레 드 간 에 서로 파 라 메 터 를 전달 하 는 지,그리고 스 레 드 에서 로 컬 변수 와 전체 공유 변수 차 이 를 소개 했다.
이 편 은 주로 스 레 드 동기 화 를 설명 한다.
공유 데이터 에 여러 스 레 드 가 동시에 접근 할 때 는 공유 데이터 가 파괴 되 지 않도록 스 레 드 로 동기 화해 야 합 니 다.여러 스 레 드 가 공유 데이터 에 동시에 접근 하지 않 으 면 스 레 드 동기 화 를 사용 하지 않 아 도 됩 니 다.
스 레 드 동기 화 에 도 문제 가 있 을 수 있 습 니 다.
  • 성능 손실.가 져 오기,잠 금 해제,스 레 드 컨 텍스트 구축 전환 은 모두 성능 을 소모 합 니 다.
  • 동기 화 는 라인 을 줄 을 서서 실행 을 기다 리 게 할 것 이다.
  • 스 레 드 동기 화 방법:
    가로막다
    스 레 드 가 Sleep,Join,EndInvoke 를 호출 하면 스 레 드 가 막 힌 상태 입 니 다(Sleep 는 스 레 드 를 막 고 Join,EndInvoke 는 다른 스 레 드 를 막 습 니 다).cpu 에서 즉시 종료 합 니 다.(차단 상태의 스 레 드 는 cpu 를 소모 하지 않 습 니 다)
    스 레 드 가 차단 과 비 차단 상태 에서 전환 할 때 몇 밀리초 의 시간 이 소 모 됩 니 다.
    
    //Join
    static void Main()
    {
     Thread t = new Thread (Go);
     Console.WriteLine ("Main      ...."); 
     t.Start();
     t.Join();//  Main  
     Console.WriteLine ("Main      ,    ...");
    }
     
    static void Go()
    {
     Console.WriteLine (" t     Go  ..."); 
    }
    
    //Sleep
    static void Main()
    {
     Console.WriteLine ("Main      ...."); 
     Thread.CurrentThread.Sleep(3000);//      
     Console.WriteLine ("Main      ,    ...");
    }
     
     //Task
     static void Main()
    {
     Task Task1=Task.Run(() => { 
      Console.WriteLine("task    ..."); 
      Thread.Sleep(1000);
      }); 
     Console.WriteLine(Task1.IsCompleted);  
     Task1.Wait();//      ,  Task1  
     Console.WriteLine(Task1.IsCompleted); 
    }
    잠 금 추가(lock)
    잠 금 을 추가 하여 여러 스 레 드 가 같은 시간 에 하나의 스 레 드 만 이 방법 을 호출 할 수 있 고 다른 스 레 드 가 막 혔 습 니 다.
    동기 화 대상 선택:
  • 참조 형식 을 사용 하고 값 형식 에 자 물 쇠 를 추가 할 때 상자 에 넣 어 새로운 대상 을 만 듭 니 다.
  • 은 private 장식 을 사용 하여 Public 를 사용 할 때 잠 금 이 생기 기 쉽다.(lock(this),lock(type:of(인 스 턴 스)를 사용 할 때 이 종 류 는 private 여야 합 니 다.
  • string 은 잠 금 대상 이 될 수 없습니다.
  • 은 lock 에서 await 키 워드 를 사용 할 수 없습니다.
  • 잠 금 은 정적 형식 이 어야 합 니까?
    잠 겨 있 는 방법 이 정적 이 라면 이 잠 금 은 정적 형식 이 어야 합 니 다.이렇게 하면 전체 국면 에서 이 방법 을 잠 그 고 이 종류 가 몇 개의 실례 가 있 든 지 간 에 줄 을 서서 실행 해 야 한다.
    잠 겨 있 는 방법 이 정적 이 아니라면 정적 형식의 잠 금 을 사용 할 수 없습니다.잠 겨 있 는 방법 은 인 스 턴 스 에 속 하기 때문에 이 인 스 턴 스 가 잠 금 방법 을 호출 하여 손상 되 지 않 으 면 됩 니 다.서로 다른 인 스 턴 스 간 에는 잠 금 이 필요 없습니다.이 자 물 쇠 는 모든 인 스 턴 스 를 잠 그 는 방법 이 아니 라 이 인 스 턴 스 만 잠 그 는 방법 입 니 다.*
    
    class ThreadSafe
    {
     private static object _locker = new object();
     
     void Go()
     {
     lock (_locker)
     {
     ......//        (Static Method),               
     }
     }
    
    private object _locker2=new object();
     void GoTo()
     {
     lock(_locker2)
     //       ,     ,      ,                 
     }
    }
    동기 화 대상 은 lock 의 대상 을 겸 할 수 있 습 니 다.
    예:
    
    class ThreadSafe
    {
     private List <string> _list = new List <string>(); 
     void Test()
     {
     lock (_list)
     {
     _list.Add ("Item 1");
     }
     }
    }
    Monitorslock 은 사실 Monitors 의 간결 한 서법 이다.
    
    lock (x) 
    { 
     DoSomething(); 
    } 
    둘 은 사실 같다.
    
    System.Object obj = (System.Object)x; 
    System.Threading.Monitor.Enter(obj); 
    try 
    { 
     DoSomething(); 
    } 
    finally 
    { 
     System.Threading.Monitor.Exit(obj); 
    } 
    상호 배척 자물쇠(Mutex)
    상호 배척 자 물 쇠 는 서로 배척 하 는 동기 화 대상 으로 같은 시간 에 있 고 하나의 스 레 드 만 이 그것 을 얻 을 수 있다.프로 세 스 단계 의 스 레 드 동기 화 를 실현 할 수 있 습 니 다.
    
    class Program
     {
     //        
     public static Mutex mutex = new Mutex();
    
     static void Main(string[] args)
     {
      for (int i = 0; i < 3; i++)
      {
      //                  
      Thread test = new Thread(MutexMethod);
      test.Start();
      }
      Console.Read();
     }
    
     public static void MutexMethod()
     {
      Console.WriteLine("{0}        ", Thread.CurrentThread.Name);
      mut.WaitOne();
      Console.WriteLine("{0}        ", Thread.CurrentThread.Name); 
      Thread.Sleep(1000);
      Console.WriteLine("{0}        ", Thread.CurrentThread.Name);
      //      
      mut.ReleaseMutex();
      Console.WriteLine("{0}        ", Thread.CurrentThread.Name);
     }
     }
    상호 배척 잠 금 은 서로 다른 프로 세 스 간 에 스 레 드 동기 화 를 실현 할 수 있다.
    상호 배척 자 물 쇠 를 사용 하여 한 번 에 한 프로그램 만 시작 할 수 있 는 기능 을 실현 합 니 다.
    
     public static class SingleInstance
     {
     private static Mutex m;
    
     public static bool IsSingleInstance()
     {
      //          
      Boolean isCreateNew = false;
      try
      {
      m = new Mutex(initiallyOwned: true, name: "SingleInstanceMutex", createdNew: out isCreateNew);
      }
      catch (Exception ex)
      {
      
      }
      return isCreateNew;
     }
     }
    상호 배척 자물쇠 의 세 개의 매개 변 수 를 가 진 구조 함수
  • initially Owned:initially Owned 가 true 라면 상호 배척 잠 금 의 초기 상 태 는 정례 화 된 스 레 드 에 의 해 가 져 오 는 것 입 니 다.그렇지 않 으 면 정례 화 된 스 레 드 는 가 져 오지 않 은 상태 입 니 다.
  • name:이 상호 배척 자물쇠 의 이름 은 운영 체제 에서 name 라 는 상호 배척 자물쇠 mutex 만 있 습 니 다.만약 에 하나의 스 레 드 가 이 name 의 상호 배척 자 물 쇠 를 얻 으 면 다른 스 레 드 는 이 상호 배척 자 물 쇠 를 얻 을 수 없습니다.그 스 레 드 가 이 스 레 드 에 풀 릴 때 까지 기 다 려 야 합 니 다.
  • createNew:지정 한 이름 의 상호 배척 체 가 존재 하면 false 로 돌아 갑 니 다.그렇지 않 으 면 true 로 돌아 갑 니 다.
  • 신호 와 핸들lockmutex 은 스 레 드 동기 화 를 실현 하여 한 번 에 한 스 레 드 만 실행 할 수 있 도록 확보한다.하지만 스 레 드 간 통신 은 이 루어 지지 않 는 다.스 레 드 가 서로 통신 해 야 한다 면 AutoResetEvent,ManualResetEvent 을 사용 하여 신 호 를 통 해 서로 통신 해 야 한다.그것들 은 모두 두 가지 상태 가 있 는데,중지 상태 와 비 중지 상태 이다.비 종료 상태 에 있 을 때 만 스 레 드 가 막 힐 수 있 습 니 다.
    AutoResetEvent:AutoResetEvent 구조 함 수 는 bool 형식의 매개 변 수 를 전달 할 수 있 고 falseAutoResetEvent 대상 의 초기 상 태 를 비 종료 로 설정 하 는 것 을 나타 낸다.true 표지 의 종료 상태 라면 WaitOne 방법 은 더 이상 스 레 드 를 막 지 않 을 것 이다.그러나 이 종 류 는 자동 으로 종료 상 태 를 비 종료 로 수정 하기 때문에 이후 WaitOne 방법 을 사용 하면 차단 된다.WaitOne 방법 이 AutoResetEvent 대상 상태 가 종료 되 지 않 으 면 이 방법 을 사용 하 는 스 레 드 를 차단 합 니 다.시간 을 지정 할 수 있 습 니 다.신 호 를 받 지 못 하면 false 로 돌아 갑 니 다.set 방법 으로 막 힌 스 레 드 를 방출 합 니 다.하지만 한 번 에 막 힌 스 레 드 만 풀 수 있 습 니 다.
    
    class ThreadSafe 
    { 
     static AutoResetEvent autoEvent; 
    
     static void Main() 
     { 
     // AutoResetEvent       
     autoEvent = new AutoResetEvent(false); 
    
     Console.WriteLine("     ..."); 
     Thread t = new Thread(DoWork); 
     t.Start(); 
    
     Console.WriteLine("   sleep 1 ..."); 
     Thread.Sleep(1000); 
    
     Console.WriteLine("       ..."); 
     autoEvent.Set(); 
     } 
    
     static void DoWork() 
     { 
     Console.WriteLine(" t    DoWork  ,      main    ..."); 
     autoEvent.WaitOne(); 
     Console.WriteLine(" t  DoWork     main    ,    ..."); 
     } 
    
    } 
    출력
    주 스 레 드 실행...
    주 스 레 드 sleep 1 초...
      t 스 레 드 실행 DoWork 방법,기본 스 레 드 신 호 를 기다 리 는 것 을 막 습 니 다...
    메 인 스 레 드 신호 방출...
      t 스 레 드 DoWork 방법 으로 main 스 레 드 신 호 를 가 져 와 계속 실행...
    ManualResetEventManualResetEventAutoResetEvent 의 용법 은 유사 하 다.AutoResetEventSet 방법 을 호출 한 후에 신 호 를 방출(종료)에서 차단(비 종료)으로 자동 으로 바 꾸 고 한 번 에 한 스 레 드 만 신 호 를 받 을 수 있 습 니 다.한편,ManualResetEventSet 방법 을 호출 한 후에 자동 으로 신 호 를 방출(종료)에서 차단(종료 되 지 않 음)으로 바 꾸 지 않 고 신 호 를 계속 방출 하여 한 번 에 여러 개의 차단 스 레 드 가 운행 되 고 Reset 방법 만 수 동 으로 호출 하여 신 호 를 방출(종료)에서 차단(종료 되 지 않 음)으로 바 꿉 니 다.이후 Wait.One 방법 을 재 호출 해 야 스 레 드 가 다시 막 힐 수 있 습 니 다.
    
    public class ThreadSafe
    {
     //            ManualResetEvent
     private static ManualResetEvent mre = new ManualResetEvent(false);
    
     static void Main()
     {
     for(int i = 0; i <= 2; i++)
     {
      Thread t = new Thread(ThreadProc);
      t.Name = "Thread_" + i;
      t.Start();
     }
    
     Thread.Sleep(500);
     Console.WriteLine("
    , , Set "); mre.Set(); Thread.Sleep(500); Console.WriteLine("
    ManualResetEvent , Wait.One , 。"); for(int i = 3; i <= 4; i++) { Thread t = new Thread(ThreadProc); t.Name = "Thread_" + i; t.Start(); } Thread.Sleep(500); Console.WriteLine("
    Reset ,ManualResetEvent , Wait.One "); mre.Reset(); Thread t5 = new Thread(ThreadProc); t5.Name = "Thread_5"; t5.Start(); Thread.Sleep(500); Console.WriteLine("
    Set , "); mre.Set(); } private static void ThreadProc() { string name = Thread.CurrentThread.Name; Console.WriteLine(name + " WaitOne()"); mre.WaitOne(); Console.WriteLine(name + " "); } } //Thread_2 WaitOne() //Thread_1 WaitOne() //Thread_0 WaitOne() // , , Set //Thread_2 //Thread_1 //Thread_0 // ManualResetEvent , Wait.One , 。 //Thread_3 WaitOne() //Thread_4 WaitOne() //Thread_4 //Thread_3 /// Reset ,ManualResetEvent , Wait.One //Thread_5 WaitOne() // Set , //Thread_5
    Interlocked
    하나의 변수 가 여러 스 레 드 로 수정 되면 읽 습 니 다.Interlocked 사용 가능 합 니 다.
    컴퓨터 에 서 는 데이터 에 대한 첨삭 이 원자 적 이라는 것 을 보장 할 수 없다.왜냐하면 데이터 에 대한 조작 도 절차 적 이기 때문이다.
  • 인 스 턴 스 변수의 값 을 레지스터 에 불 러 옵 니 다.
  • 이 값 을 증가 하거나 감소 합 니 다.
  • 인 스 턴 스 변수 에 이 값 을 저장 합 니 다.
  • Interlocked 은 다 중 스 레 드 가 공유 하 는 변수 에 원자 조작 을 제공 합 니 다.Interlocked 은 원자 조작 이 필요 한 방법 을 제공 했다.
  • public static int Add (ref int location1, int value); 두 개의 매개 변 수 를 더 하고 결과 와 값 을 이 첫 번 째 매개 변수 에 부여 합 니 다.
  • public static int Increment (ref int location); 증가 하 다.
  • public static int CompareExchange (ref int location1, int value, int comparand);
  • location 1 과 comparand 를 비교 하면 value 로 대 체 됩 니 다.
    value 첫 번 째 매개 변수 가 세 번 째 매개 변수 와 같다 면 value 를 첫 번 째 매개 변수 에 할당 합 니 다.
    comparand 와 첫 번 째 매개 변 수 를 비교 합 니 다.
    ReaderWriterLock
    자원 이나 데 이 터 를 방문 하기 전에 최신 으로 확보 하려 면그러면 ReaderWriterLock 을 사용 할 수 있 습 니 다.이 자 물 쇠 는 자원 에 대한 할당 이나 업 데 이 트 를 가 져 올 때 자신 만 이 자원 에 접근 할 수 있 고 다른 스 레 드 에 접근 할 수 없습니다.자물쇠그러나 이 데 이 터 를 자물쇠 로 읽 을 때 열 쇠 를 실현 할 수 없습니다.lock 은 같은 시간 에 하나의 스 레 드 만 실행 할 수 있 습 니 다.한편,ReaderWriterLock 은 같은 시간 에 여러 개의 스 레 드 가 읽 기 동작 을 수행 할 수 있 거나 잠 겨 있 는 스 레 드 만 쓰기 동작 을 수행 할 수 있 습 니 다.
    
     class Program
     {
     //       
     public static ReaderWriterLock readerwritelock = new ReaderWriterLock();
     static void Main(string[] args)
     {
      //          
      Thread t1 = new Thread(Write);
      // t1.Start(1);
      Thread t2 = new Thread(Write);
      //t2.Start(2);
      //   10       
      for (int i = 3; i < 6; i++)
      {
      Thread t = new Thread(Read);
      // t.Start(i);
      }
    
      Console.Read();
    
     }
    
     //     
     public static void Write(object i)
     {
      //      ,20    。
      Console.WriteLine("  :" + i + "   ...");
      readerwritelock.AcquireWriterLock(Timeout.Infinite);
      Console.WriteLine("  :" + i + "    " + DateTime.Now);
      //      
      Console.WriteLine("  :" + i + "   ...");
      Thread.Sleep(1000);
      readerwritelock.ReleaseWriterLock();
    
     }
    
     //     
     public static void Read(object i)
     {
      Console.WriteLine("  :" + i + "   ...");
    
      //      ,20    
      readerwritelock.AcquireReaderLock(Timeout.Infinite);
      Console.WriteLine("  :" + i + "    " + DateTime.Now);
      //      
      Console.WriteLine("  :" + i + "   ...");
      Thread.Sleep(1000);
    
      readerwritelock.ReleaseReaderLock();
    
     }
     }
    //    writer reader  。         writer    。 reader     。
    
    //  reader  
    //  :1   ...
    //  :1    2017/7/5 17:50:01
    //  :1   ...
    //  :2   ...
    //  :2    2017/7/5 17:50:02
    //  :2   ...
    
    //  writer  
    //  :3   ...
    //  :5   ...
    //  :4   ...
    //  :5    2017/7/5 17:50:54
    //  :5   ...
    //  :3    2017/7/5 17:50:54
    //  :3   ...
    //  :4    2017/7/5 17:50:54
    //  :4   ...
    참고:
  • MSDN
  • 《CLR via C#》
  • 이상 은 C\#스 레 드 동기 화 에 대한 상세 한 내용 을 깊이 분석 하고 c\#스 레 드 동기 화 에 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!

    좋은 웹페이지 즐겨찾기