C\#중 Timer 사용 및 재 입 문제 해결

9418 단어 C#timer
★프롤로그
오 랜 만 에 라 이브 라이터 도 열 고 블 로그 도 안 쓴 지 오래 됐 는데 정말 게 을 러 요.잔말 말고 이번 블 로그 의 주제 인Timer에 바로 들 어가 세 요.왜 이 글 을 써 야 하 는가.며칠 전 친구 의 요청 으로'해커'라 는 작은 도구 가 되 고 싶 었 기 때문이다.기능 은 간단 하 다.클립보드 의 내용 을 자동 으로 가 져 와 메 일 을 보 내 려 면 Timer 로 클립보드 의 내용 을 순환 적 으로 가 져 와 야 하 는데 메 일 을 보 내 는 기능 이 있어 서 C\#SmtpClient로 메 일 을 보 내지 못 했다.예전 에 메 일 을 보 내 는 것 과 비슷 한 기능 을 쓴 적 이 있 는데 그 때 는 인터넷 으로 쉽게 할 수 있 었 는데 지금 은 사용 할 수 없어 서 어떻게 된 일 인지 모 르 겠 어 요.그만 둘 수 밖 에 없 었 어 요.타이머 사용 중 이전에 생각 하지 못 했 던 문제 인 재 입 을 만 났 다.
★소개
먼저 timer 를 간단하게 소개 합 니 다.여기 서 말 하 는 timer 는 System.Timers.timer 를 말 합 니 다.말 그대로 지 정 된 간격 에서 사건 을 일 으 킬 수 있 습 니 다.공식 소 개 는 여기 서 다음 과 같다.
Timer 구성 요 소 는 서버 기반 타이머 입 니 다.프로그램 에서 Elapsed 이 벤트 를 일 으 키 는 주기 적 인 간격 을 지정 할 수 있 습 니 다.그리고 이 사건 을 처리 함으로써 일반적인 처 리 를 제공 할 수 있다.예 를 들 어 관건 적 인 서버 가 있다 고 가정 하면 매주 7 일,매일 24 시간 운행 해 야 합 니 다.정기 적 으로 서버 를 검사 하고 시스템 이 켜 지고 실행 되 는 지 확인 하기 위해 Timer 를 사용 하 는 서 비 스 를 만 들 수 있 습 니 다.시스템 이 응답 하지 않 으 면 이 서 비 스 는 서버 를 다시 시작 하거나 관리자 에 게 알 릴 수 있 습 니 다.서버 기반 Timer 는 다 중 스 레 드 환경 에서 보조 스 레 드 에 사용 하기 위해 설계 되 었 습 니 다.서버 타 이 머 는 온라인 으로 이동 하여 발생 하 는 Elapsed 사건 을 처리 할 수 있 으 며,이 는 윈도 타이머 보다 정확 한 시간 에 사건 을 일 으 킬 수 있다.
다른 timer 와 어떤 차이 가 있 는 지 알 고 싶다 면 여 기 를 보 세 요.안에 상세 한 소개 가 있 습 니 다.더 이상 말 하지 않 겠 습 니 다.그럼 이 타 이 머 를 사용 하면 어떤 좋 은 점 이 있 나 요?주로.NET Thread Pool 을 통 해 이 루어 진 경 량,시간 계산 이 정확 하고 응용 프로그램 과 정보 에 대해 특별한 요구 가 없 기 때문이다.
★사용
다음은 간단하게 소개 하 겠 습 니 다.이 Timer 를 어떻게 사용 하 는 지,사실은 아주 간단 합 니 다.저 는 마이크로소프트 가 제공 한 예 를 들 어 테스트 를 하고 코드 를 직접 올 렸 습 니 다.

//Timer         ,    GC  
 private static System.Timers.Timer aTimer;
 public static void Main()
 {
 //   Timer ,       10000  ; 
 aTimer = new System.Timers.Timer(10000);
 //        
 aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
 //       2 (2000  ),           
 aTimer.Interval = 2000;
 //       (false)      (true),   true
 aTimer.AutoReset = true;
 //    
 aTimer.Enabled = true;
 Console.WriteLine("        。");
 Console.ReadLine();
 }
 //  Timer     
 private static void OnTimedEvent(object source, ElapsedEventArgs e)
 {
 Console.WriteLine("        : {0}", e.SignalTime);
 }
실행 결 과 는 다음 과 같 습 니 다.시간 계산 이 정확 합 니 다.

/*
        。
        : 2014/12/26     23:08:51
        : 2014/12/26     23:08:53
        : 2014/12/26     23:08:55
        : 2014/12/26     23:08:57
        : 2014/12/26     23:08:59
*/
★재 입 문제 재현 및 분석
재 입 이 라 니 요?이것 은 다 중 스 레 드 프로 그래 밍 에 관 한 개념 입 니 다.프로그램 에서 여러 스 레 드 가 동시에 실 행 될 때 같은 방법 이 여러 프로 세 스 에 의 해 동시에 호출 될 수 있 습 니 다.이 방법 에 비 스 레 드 안전 코드 가 존재 할 때 방법 재 입 은 데이터 가 일치 하지 않 는 상황 을 초래 할 수 있 습 니 다.Timer 방법 재 입 은 다 중 스 레 드 타 이 머 를 사용 하 는 것 을 말 합 니 다.하나의 Timer 처리 가 아직 완성 되 지 않 았 고 시간 이 되면 다른 Timer 는 이 방법 에 계속 들 어가 처리 할 것 입 니 다.다음은 재 입 문제 의 발생 을 보 여 드 리 겠 습 니 다.

//               
 private static int outPut = 1;
 //  ,timer        1
 private static int num = 0;
 private static System.Timers.Timer timer = new System.Timers.Timer();
 public static void Main()
 {
 timer.Interval = 1000;
 timer.Elapsed += TimersTimerHandler;
 timer.Start();
 Console.WriteLine("        。");
 Console.ReadLine();
 }
 /// <summary>
 /// System.Timers.Timer     
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="args"></param>
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num;
 Console.WriteLine(string.Format("  {0}  :{1},     :{2}", t, outPut.ToString(),DateTime.Now));
 System.Threading.Thread.Sleep(2000);
 outPut++;
 Console.WriteLine(string.Format("  {0}  1   :{1},    :{2}", t, outPut.ToString(),DateTime.Now));
 }
출력 결 과 를 보 여 줍 니 다:

위의 출력 결과 가 이상 하 다 고 생각 하지 않 습 니까?먼저 스 레 드 1 출력 이 1 이 고 문제 가 없습니다.그리고 2 초 후에 스 레 드 1 이 1 이 증가 한 후에 출력 이 2 입 니 다.이것 은 문제 가 있 습 니 다.중간 에 왜 스 레 드 2 의 출력 이 나 타 났 습 니까?더 이상 한 것 은 스 레 드 2 가 처음에 1 로 출력 되 었 는데 1 이 증가 한 후에 3 이 되 었 다 는 것 이다!사실 이것 이 재 입 으로 인 한 문제 다.서 두 르 지 마라,우리 가 분석 해 보면 그 이 유 를 알 수 있 을 것 이다.
먼저 timer 가 시간 을 계산 한 후에 스 레 드 1 실행 방법 을 엽 니 다.스 레 드 1 이 처음 출력 한 후에 스 레 드 1 은 2 초 동안 휴면 합 니 다.이때 timer 는 가만히 있 지 않 습 니 다.설정 한 시간 이 1 초 간격 이기 때문에 스 레 드 1 이 1 초 동안 휴면 한 후에 timer 는 스 레 드 2 실행 방법 을 열 었 습 니 다.스 레 드 2 는 스 레 드 1 이 실행 중이 든 휴면 상태 든 상관 하지 않 습 니 다.그래서 이때 스 레 드 2 의 출력 도 1 이다.스 레 드 1 은 휴면 상태 이기 때문에 스스로 증가 하지 않 았 다.그리고 1 초 간격 으로 두 사건 이 동시에 발생 했 습 니 다.스 레 드 1 이 휴면 상태 가 지나 면 자동 으로 출력 이 2 가 되 고 timer 가 동시에 하나의 스 레 드 3 을 열 었 습 니 다.스 레 드 3 출력 은 스 레 드 1 이 증가 한 후의 값 2 이 고 1 초 가 지 났 습 니 다.스 레 드 2 가 휴면 상태 가 지 났 습 니 다.예전 의 출력 은 2 가 되 었 기 때문에 증가 한 후에 출력 은 3 이 고 1 초 가 지 났 습 니 다.저 는 어 지 러 울 것 같 습 니 다.아마 이런 뜻 일 것 입 니 다.내 가 표현 하고 싶 은 것 은 하나의 Timer 가 열 린 스 레 드 처리 가 아직 완성 되 지 않 았 고 시간 이 되면 다른 Timer 는 이 방법 에 계속 들 어가 처리 할 것 이다.
그럼 이 문 제 를 어떻게 해결 합 니까?해결 방안 은 세 가지 가 있 습 니 다.다음 과 같이 서로 다른 장면 에 적응 하지만 마지막 을 추천 하 는 것 이 안전 합 니 다.
★재 입 문제 해결 방안
1.lock(Object)의 방법 으로 재 입 을 방지 하 는 것 은 하나의 Timer 처리 가 실행 되 고 있 음 을 나타 낸다.다음 Timer 가 발생 했 을 때 지난번 에 실행 이 끝나 지 않 았 을 때 실행 을 기다 리 고 있 는 것 을 발견 하면 자주 나타 나 지 않 는 장면(구체 적 으로 연구 한 적 이 없고 메모리 에서 차지 하 는 것 같다)을 적용 한다.
코드 는 위 와 차이 가 많 지 않 습 니 다.트리거 하 는 방법 에 lock 을 추가 합 니 다.그러면 스 레 드 2 가 트리거 하 는 방법 에 들 어가 면 잠 겨 있 는 것 을 발견 하고 잠 겨 있 는 코드 가 처리 되 기 를 기다 릴 것 입 니 다.코드 는 다음 과 같 습 니 다.

private static object locko = new object(); 
 /// <summary>
 /// System.Timers.Timer     
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="args"></param>
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num; 
 lock (locko)
 { Console.WriteLine(string.Format("  {0}  :{1},     :{2}", t, outPut.ToString(), DateTime.Now)); System.Threading.Thread.Sleep(2000); outPut++; Console.WriteLine(string.Format("  {0}  1   :{1},    :{2}", t, outPut.ToString(), DateTime.Now)); } }
 실행 결과:

 2.로 고 를 설정 하면 타이머 처리 가 실행 되 고 있 음 을 나타 낸다.다음 타이머 가 발생 했 을 때 이전 이 실행 되 지 않 고 포기 한 것 을 발견 했다.(여 기 는 포기 하 는 것 이지 기다 리 는 것 이 아니 라 실행 결 과 를 보면 무슨 뜻 인지 알 수 있다.)실행 은 자주 나타 나 는 장면 에 적용 된다.코드 는 다음 과 같 습 니 다:

 private static int inTimer = 0; 
 /// <summary>
 /// System.Timers.Timer     
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="args"></param>
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num;
 if (inTimer == 0)
 {
 inTimer = 1;
 Console.WriteLine(string.Format("  {0}  :{1},     :{2}", t, outPut.ToString(), DateTime.Now));
 System.Threading.Thread.Sleep(2000);
 outPut++;
 Console.WriteLine(string.Format("  {0}  1   :{1},    :{2}", t, outPut.ToString(), DateTime.Now));
 inTimer = 0;
 }
 }
실행 결과:

3.다 중 스 레 드 에서 inTimer 에 게 값 을 부여 하 는 것 이 안전 하지 않 습 니 다.Interlocked.Exchange 는 경량급 스 레 드 가 대상 에 게 안전하게 값 을 부여 하 는 방법(느낌 이 비교적 높 고 추천 하 는 방법)을 제공 합 니 다.집행 결 과 는 방법 2 와 마찬가지 로 집행 을 포기 합 니 다.Interlocked.Exchange 용법 은 여 기 를 참고 하 세 요.

private static int inTimer = 0; 
 /// <summary>
 /// System.Timers.Timer     
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="args"></param>
 private static void TimersTimerHandler(object sender, EventArgs args)
 {
 int t = ++num;
 if (Interlocked.Exchange(ref inTimer, 1) == 0)
 {
 Console.WriteLine(string.Format("  {0}  :{1},     :{2}", t, outPut.ToString(), DateTime.Now));
 System.Threading.Thread.Sleep(2000);
 outPut++;
 Console.WriteLine(string.Format("  {0}  1   :{1},    :{2}", t, outPut.ToString(), DateTime.Now));
 Interlocked.Exchange(ref inTimer, 0); 
 }
 }
실행 결과:
★총괄
드디어 코드 가 끝 났 으 니 정말 쉽 지 않 군요.블 로 그 를 쓰 는 것 은 매우 정력 을 소모 하 는 일이 다.그 큰 소 들 이 끊임없이 글 을 쓰 는 것 에 진심으로 탄복 하고 경 의 를 표 한다!여기 서 조금 요약 하면 timer 는 사용 이 매우 간단 한 유형 입 니 다.가 져 오 면 바로 사용 할 수 있 습 니 다.여 기 는 주로 timer 를 사용 할 때 문 제 를 다시 해결 하 는 것 을 총 결 했 습 니 다.예전 에 도 이 문 제 를 생각해 본 적 이 없고 해결 방안 도 간단 합 니 다.여기 세 가지 가 있 는데 다른 방법 이 있 는 지 모 르 겠 습 니 다.이곳 의 해결 방안 은 다 중 스 레 드 의 재 입 문제 도 적용 된다.
★참조
여기에 글 에 언급 되 지 않 은 참고 사항 을 열거 합 니 다.선배 님 들 의 지혜 로 운 결정 에 감 사 드 립 니 다!
ASP.NET 타이머 리 셋 방법의 리 셋
이상 은 본 고의 모든 내용 입 니 다.본 고의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.또한 저 희 를 많이 지지 해 주시 기 바 랍 니 다!

좋은 웹페이지 즐겨찾기