C\#스 레 드 동기 화 상세 설명

머리말
스 레 드 탱크 의 스 레 드 가 막 힐 때 스 레 드 탱크 는 추가 스 레 드 를 만 들 고 스 레 드 를 만 들 고 소각 하 며 스 레 드 를 예약 하 는 데 상당히 비 싼 메모리 자원 이 필요 합 니 다.또한 많은 개발 자 들 이 자신의 프로그램의 스 레 드 가 유용 한 일 을 하지 않 은 것 을 볼 때 더 많은 스 레 드 를 만 드 는 습관 이 있 습 니 다.신축 가능 하고 민감 한 프로그램 을 구축 하기 위해 앞에서 소개 하 였 습 니 다C\#비동기 프로 그래 밍 상세 설명
그러나 비동기 프로 그래 밍 역시 심각 한 문제 가 존재 합 니 다.만약 에 두 개의 서로 다른 스 레 드 가 같은 변수 와 데 이 터 를 방문 하면 우리 의 비동기 함수 의 실현 방식 에 따라 두 개의 스 레 드 가 같은 데 이 터 를 동시에 방문 할 수 없습니다.이때 우 리 는 스 레 드 동기 화가 필요 합 니 다.여러 스 레 드 가 동시에 공유 데 이 터 를 방문 할 때 스 레 드 동기 화 는 데이터 손상 을 방지 할 수 있 습 니 다.이 개념 을 강조 하 는 이 유 는 스 레 드 동기 화 본질 이 바로 시간 계산 문제 이기 때 문 입 니 다.
비동기 와 동기 화 는 상대 적 인 것 이 고 동기 화 는 순서대로 실행 되 며 하 나 를 실행 하고 다음 을 실행 하려 면 기다 리 고 조율 해 야 한다.이 보 는 서로 독립 하고 어떤 사건 을 기다 리 는 과정 에서 자신의 일 을 계속 하 는 것 이다.이 사건 이 끝 난 후에 일 할 필요 가 없다.스 레 드 는 바로 비동기 적 인 방식 을 실현 하 는 것 이다.비동기 화 는 호출 방법의 메 인 스 레 드 가 다른 스 레 드 의 완성 을 동시에 기다 리 지 않 아 도 메 인 스 레 드 가 다른 일 을 할 수 있 도록 하 는 것 이다.
기본 사용자 모드 와 커 널 모드 구조
기초 개념
기원:코드 에서 사용 할 수 있 는 간단 한 구조
사용자 모드:특수 한 CPU 명령 을 통 해 스 레 드 를 조정 합 니 다.운영 체 제 는 기본 사용자 모드 의 구조 에서 스 레 드 가 차단 되 는 것 을 영원히 감지 하지 못 합 니 다.
커 널 모드:windows 자체 에서 제공 하고 응용 프로그램의 스 레 드 에서 커 널 로 이 루어 진 함 수 를 호출 합 니 다.
사용자 모드 구조
변형 구조
C\#컴 파일 러,JIT 컴 파일 러 와 CPU 는 모두 코드 를 최적화 시 킬 것 이다.그들 은 우리 의 의 도 를 최대한 보류 할 것 이다.그러나 다 중 스 레 드 의 측면 에서 볼 때 우리 의 의 도 는 반드시 보류 되 는 것 이 아니다.다음 과 같은 예 를 들 어 설명 한다.

 static void Main(string[] args)
 {
 Console.WriteLine(" worker    5s   ");
 var t = new Thread(Worker);
 t.Start();
 Thread.Sleep(5000);
 stop = true;
 Console.ReadLine();
 }
 private static bool stop = false;
 private static void Worker(object obj)
 {
 int x = 0;
 while (!stop)
 {
 x++;
 }
 Console.WriteLine("worker    x={0}",x);
 }
 

컴 파일 러 가 stop 이 false 인 것 을 검사 하면 코드 를 생 성하 여 무한 순환 에 들 어가 고 순환 에서 x 를 계속 증가 하기 때문에 최적화 순환 이 빨리 완성 되 지만 컴 파일 러 는 stop 을 한 번 만 검 측 하고 매번 검 측 하 는 것 이 아니다.
예 2--두 스 레 드 를 동시에 방문 합 니 다.

class test
 {
 private static int m_flag = 0;
 private static int m_value = 0;
 public static void Thread1(object obj)
 {
 m_value = 5;
 m_flag = 1;
 }
 public static void Thread2(object obj)
 {
 if (m_flag == 1)
 Console.WriteLine("m_value = {0}", m_value);
 }
 //  CPU            
 public void Exec()
 {
 var thread1 = new Thread(Thread1);
 var thread2 = new Thread(Thread2);
 thread1.Start();
 thread2.Start();
 Console.ReadLine();
 }
 }
프로그램 이 실 행 될 때 컴 파일 러 는 변 수 를 mflag 와 mvalue 는 RAM 에서 CPU 레지스터 를 읽 고 RAM 은 m 를 먼저 전달 합 니 다.value 의 값 0,thread 1 은 값 을 5 로 바 꿉 니 다.그러나 thread 2 는 thread 2 가 여전히 값 이 0 이 라 고 생각 하 는 지 모 릅 니 다.이런 문 제 는 일반적으로 다 핵 CPU 에서 발생 할 확률 이 높 습 니 다.CPU 가 많 을 수록 여러 스 레 드 가 동시에 자원 에 접근 할 확률 이 높 습 니 다.
키워드 volatile 은 C\#컴 파 일 러,JTP 컴 파 일 러,CPU 가 실행 하 는 최 적 화 를 금지 하 는 역할 을 합 니 다.변 수 를 사용 하면 CPU 레지스터 에 필드 캐 시 를 허용 하지 않 고 필드 의 읽 기와 쓰기 가 RAM 에서 진행 되도록 합 니 다.
상호 잠 금 구조
System.Threading.Interlocked 클래스 의 모든 방법 은 원자의 읽 기와 쓰기 동작 을 한 번 씩 수행 합 니 다.Interlocked 방법 을 호출 하기 전의 모든 변 수 는 이 Interlocked 방법 이 호출 되 기 전에 실 행 됩 니 다.호출 된 모든 변 수 는 이 호출 후에 읽 습 니 다.
Interlocked 방법 은 주로 INT 32 변수 에 대해 정적 조작 Add,Decrement,Compare,Exchange,CompareChange 등 방법 을 하고 object,Double 등 유형의 매개 변 수 를 받아들인다.
원자 조작:스 레 드 스케줄 러 에 의 해 중단 되 지 않 는 조작 을 말한다.이 동작 은 시작 하면 끝 날 때 까지 실 행 됩 니 다.중간 에 context switch(다른 스 레 드 로 전환)가 없습니다.
코드 데모:
설명:Interlocked 방법 을 통 해 몇 개의 웹 서버 를 비동기 로 조회 하고 데 이 터 를 동시에 되 돌려 주 며 결 과 는 한 번 만 실 행 됩 니 다.

//      
 enum CoordinationStatus
 {
 Cancel,
 Timeout,
 AllDone
 }

class AsyncCoordinator
 {
 //AllBegun     JustEnded    
 private int _mOpCount = 1;
 //0=false,1=true
 private int _mStatusReported = 0;
 private Action<CoordinationStatus> _mCallback;
 private Timer _mTimer;
 //          
 public void AboutToBegin(int opsToAdd = 1)
 {
 Interlocked.Add(ref _mOpCount, opsToAdd);
 }
 //              
 public void JustEnded()
 {
 if (Interlocked.Decrement(ref _mOpCount) == 0)
 {
 ReportStatus(CoordinationStatus.AllDone);
 } 
 }
 //               
 public void AllBegin(Action<CoordinationStatus> callback, int timeout = Timeout.Infinite)
 {
 _mCallback = callback;
 if (timeout != Timeout.Infinite)
 {
 _mTimer = new Timer(TimeExpired, null, timeout, Timeout.Infinite);
 JustEnded();
 }
 }
 private void TimeExpired(object o)
 {
 ReportStatus(CoordinationStatus.Timeout);
 }
 public void Cancel()
 {
 ReportStatus(CoordinationStatus.Cancel);
 }
 private void ReportStatus(CoordinationStatus status)
 {
 //         ,    ,      ,     
 if (Interlocked.Exchange(ref _mStatusReported, 1) == 0)
 {
 _mCallback(status);
 } 
 }
 }

class MultiWebRequest
 {
 //               
 private AsyncCoordinator _mac = new AsyncCoordinator();
 protected Dictionary<string,object> _mServers = new Dictionary<string, object>
 {
 {"http://www.baidu.com",null},{"http://www.Microsoft.com",null},{"http://www.cctv.com",null},
 {"http://www.souhu.com",null},{"http://www.sina.com",null},{"http://www.tencent.com",null},
 {"http://www.youku.com",null}
 };
 private Stopwatch sp;
 public MultiWebRequest(int timeout = Timeout.Infinite)
 {
 sp = new Stopwatch();
 sp.Start();
 //             
 var httpclient = new HttpClient();
 foreach (var server in _mServers.Keys)
 {
 _mac.AboutToBegin(1);
 httpclient.GetByteArrayAsync(server).ContinueWith(task => ComputeResult(server, task));
 }
 _mac.AllBegin(AllDone,timeout);
 Console.WriteLine("");
 }
 private void ComputeResult(string server, Task<Byte[]> task)
 {
 object result;
 if (task.Exception != null)
 {
 result = task.Exception.InnerException;
 }
 else
 {
 //     IO
 result = task.Result.Length;
 }
 //         
 _mServers[server] = result;
 _mac.JustEnded();
 }
 public void Cancel()
 {
 _mac.Cancel();
 }
 private void AllDone(CoordinationStatus status)
 {
 sp.Stop();
 Console.WriteLine("      {0}",sp.Elapsed);
 switch (status)
 {
 case CoordinationStatus.Cancel:
  Console.WriteLine("    ");
  break;
 case CoordinationStatus.AllDone:
  Console.WriteLine("    ,       ");
  foreach (var server in _mServers)
  {
  Console.WriteLine("{0}",server.Key);
  object result = server.Value;
  if (result is Exception)
  {
  Console.WriteLine("    {0}",result.GetType().Name);
  }
  else
  {
  Console.WriteLine("      :{0}",result);
  }
  }
  break;
 case CoordinationStatus.Timeout:
  Console.WriteLine("    ");
  break;
 default:
  throw new ArgumentOutOfRangeException("status", status, null);
 }
 }
 }
이상 의 코드 를 참고 하 시 는 것 을 권장 합 니 다.저 는 서버 에 접근 할 때 도 이 모델 을 자주 참고 합 니 다.
간단 한 자전거 자물쇠

class SomeResource
 {
 private SimpleSpinLock s1 = new SimpleSpinLock();
 public void AccessResource()
 {
 s1.Enter();
 //              
 s1.Leave();
 }
 }
 class SimpleSpinLock
 {
 private int _mResourceInUse;
 public void Enter()
 {
 while (true)
 {
 if(Interlocked.Exchange(ref _mResourceInUse,1)==0)
  return;
 }
 }
 public void Leave()
 {
 Volatile.Write(ref _mResourceInUse,1);
 }
 }
이것 은 바로 스 레 드 동기 자물쇠 의 간단 한 실현 이다.이런 자물쇠 의 가장 큰 문 제 는 경쟁 이 존재 하 는 상황 에서 스 레 드 의'자전'을 초래 할 수 있다 는 것 이다.이것 은 CPU 의 소중 한 시간 을 낭비 하고 CPU 를 조직 하여 더 많은 일 을 할 수 있 기 때문에 이런 자전 자 물 쇠 는 매우 빠 른 코드 를 보호 하 는 데 사용 해 야 한다.
이상 은 본 고의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.또한 저 희 를 많이 지지 해 주시 기 바 랍 니 다!

좋은 웹페이지 즐겨찾기