2009-11-10 5 views
1

Daniel Solis (우수 도서, btw)가 Illustrated C# 2008을 읽었으며 이벤트에 대해 더 많은 시간을 할애하여 주제에 대한 이해를 높이기로 결정했습니다. 나는 왜 내가 프로그램을 실행할 때마다 다른 결과를보고 있는지, 그리고 내가 이것을 어떻게 배울 수 있는지 이해하려고 노력 중이다.왜 이러한 타이머 이벤트가 일관성없는 시간에 발생합니까?

(소스 코드 아래)이 책의 샘플 코드에는 MyTimerClassSystem.Timers.Timer에 가입 한 일정이 있습니다. ClassA와 ClassB의 두 가지 클래스가 있으며 둘 모두 콘솔에 쓰는 이벤트 핸들러 (하나는 정적이지만 다른 하나는 정적 임)가 있습니다. 프로그램의 main 함수에서 해당 클래스의 이벤트 처리기는 MyTimerClass의 인스턴스에 이벤트에 묶입니다. 다른 함수가 람다 식을 통해 이벤트 멤버에 추가됩니다.

작성자의 코드를 사용한 후에 다른 클래스 인 ClassC을 추가하기로 결정했습니다. 프로그램의 Main 함수 내에서 이벤트 처리기를 추가하는 대신 MyTimerClass의 이벤트에 가입 된 ClassC의 생성자 내에 별도의 MyTimerClass 개체를 만들기로 결정했습니다.

3 회에 걸쳐 4.25 초 동안 코드를 실행했을 때 결과는 항상 다른 순서로 나타납니다. 순서의 Main 기능에서 사건은 항상 ClassA, ClassB, 그 후에 동일한 순서로 람다를 호출합니다. 그러나 ClassC에 대한 다른 이벤트는 항상 완전 무작위 순서로 호출되는 것처럼 보입니다. 또한 첫 번째 그룹의 메소드 호출은 약간 다른 시간을 갖는 것으로 보이지만 후속 그룹은 모두 같은 시간을 갖는 것으로 나타났습니다. 왜 그런가요?

(1) Event 1 - ClassA - 51:259 
    Event 2 - ClassC - 51:259 
(2) Event 1 - ClassB - 51:261 
(3) Event 1 - Lambda - 51:262 
(1) Event 1 - ClassA - 52:271 
(2) Event 1 - ClassB - 52:271 
(3) Event 1 - Lambda - 52:271 
    Event 2 - ClassC - 52:271 
(1) Event 1 - ClassA - 53:285 
(2) Event 1 - ClassB - 53:285 
(3) Event 1 - Lambda - 53:285 
    Event 2 - ClassC - 53:285 
(1) Event 1 - ClassA - 54:299 
(2) Event 1 - ClassB - 54:299 
(3) Event 1 - Lambda - 54:299 
    Event 2 - ClassC - 54:299 

(1) Event 1 - ClassA - 17:30 
    Event 2 - ClassC - 17:30 
(2) Event 1 - ClassB - 17:32 
(3) Event 1 - Lambda - 17:33 
(1) Event 1 - ClassA - 18:42 
(2) Event 1 - ClassB - 18:42 
(3) Event 1 - Lambda - 18:42 
    Event 2 - ClassC - 18:42 
(1) Event 1 - ClassA - 19:56 
(2) Event 1 - ClassB - 19:56 
(3) Event 1 - Lambda - 19:56 
    Event 2 - ClassC - 19:56 
    Event 2 - ClassC - 20:70 
(1) Event 1 - ClassA - 20:70 
(2) Event 1 - ClassB - 20:70 
(3) Event 1 - Lambda - 20:70 

(1) Event 1 - ClassA - 45:220 
    Event 2 - ClassC - 45:221 
(2) Event 1 - ClassB - 45:223 
(3) Event 1 - Lambda - 45:223 
(1) Event 1 - ClassA - 46:232 
(2) Event 1 - ClassB - 46:232 
(3) Event 1 - Lambda - 46:232 
    Event 2 - ClassC - 46:232 
    Event 2 - ClassC - 47:246 
(1) Event 1 - ClassA - 47:246 
(2) Event 1 - ClassB - 47:246 
(3) Event 1 - Lambda - 47:246 
(1) Event 1 - ClassA - 48:260 
(2) Event 1 - ClassB - 48:260 
(3) Event 1 - Lambda - 48:260 
    Event 2 - ClassC - 48:260 

여기에 소스 코드 내 콘솔 응용 프로그램을위한이다 :

class Program 
{ 
    static void Main(string[] args) 
    { 
     MyTimerClass mc = new MyTimerClass(); 
     ClassA ca = new ClassA(); 
     ClassC cc = new ClassC(); 

     mc.MyElapsed += ca.TimerHandlerA; 
     mc.MyElapsed += ClassB.TimerHandlerB; 
     mc.MyElapsed += (obj, e) => 
      { 
       Console.WriteLine("(3) Event 1 - Lambda - {0}:{1}", 
        System.DateTime.Now.Second, 
        System.DateTime.Now.Millisecond); 
      }; 

     Thread.Sleep(4250); 
    } 
} 

class ClassA 
{ 
    public void TimerHandlerA(Object obj, EventArgs e) 
    { 
     Console.WriteLine("(1) Event 1 - ClassA - {0}:{1}", 
      System.DateTime.Now.Second, 
      System.DateTime.Now.Millisecond); 
    } 
} 

class ClassB 
{ 
    public static void TimerHandlerB(Object obj, EventArgs e) 
    { 
     Console.WriteLine("(2) Event 1 - ClassB - {0}:{1}", 
      System.DateTime.Now.Second, 
      System.DateTime.Now.Millisecond); 
    } 
} 

class ClassC 
{ 
    public void TimerHandlerC(Object obj, EventArgs e) 
    { 
     Console.WriteLine(" Event 2 - ClassC - {0}:{1}", 
      System.DateTime.Now.Second, 
      System.DateTime.Now.Millisecond); 
    } 

    public ClassC() 
    { 
     // This will create a separate MyTimerClass and 
     // attach ClassC's event handler to mc's event. 
     MyTimerClass mc = new MyTimerClass(); 
     mc.MyElapsed += TimerHandlerC; 
    } 
} 

public class MyTimerClass 
{ 
    public event EventHandler MyElapsed; 

    private void OnOneSecond(Object obj, EventArgs e) 
    { 
     if (MyElapsed != null) 
      MyElapsed(obj, e); 
    } 

    private System.Timers.Timer MyPrivateTimer; 

    public MyTimerClass() 
    { 
     MyPrivateTimer = new System.Timers.Timer(); 

     // This will attach the OnOneSecond Event Handler 
     // to the system timer which will then raise 
     // MyElapsed. 
     MyPrivateTimer.Elapsed += OnOneSecond; 

     // This sets the interval at 1 second. 
     MyPrivateTimer.Interval = 1000; 

     // This turns the timer on when the the class 
     // is instantiated. 
     MyPrivateTimer.Enabled = true; 
    } 
} 

세 가지 질문 :

  • 왜이 결과는 매번 다른 어떤 그 일이 원인이된다?
  • 결과의 첫 번째 블록에서 시간이 약간 떨어져 있고 후속 블록이 같은 시간을 갖는 이유는 무엇입니까?
  • 이 예제에서 무엇을 배워야합니까?

답변

5

짧은 대답은 "Windows 타이머가 작동하는 방식입니다." 당신은 여기서 아무런 잘못을하지 않고 있으며, 당신이 보는 결과는 정상적인 행동입니다.

Windows에서 Microsoft가 제공하는 원시 타이머는 똑딱 똑딱 거리는 것은 아닙니다. 1000 밀리 초마다 타이머를 설정하면 이상 ( 1000 밀리 초)이 보장되지만 은 정확히 (1000 밀리 초)이 아닙니다. 대개 아주 가깝고 때로는 정확하지만 매우 정밀한 타이밍이 필요한 경우 다른 메커니즘을 조사해야합니다. Win32 C/C++ API 수준에서 적절한 메커니즘은 QueryPerformanceFrequency/QueryPerformanceCounter 메서드입니다. .NET에서이 기능을 사용하려면 StopWatch 클래스를 사용해야합니다.

주문의 경우 .NET에서 하나의 타이머에 여러 핸들러를 연결하는 경우 특정 순서대로 실행된다는 보장이 없다고 확신합니다.Microsoft가 알고리즘을 실행하는 데 어떤 알고리즘을 사용하고 있는지 정확히 알지 못하지만 결과에 따라 알고리즘을 순서대로 수행 할 수있는 알고리즘이 아니라는 사실이 명확하게 나타납니다. 코드를 순서대로 정렬해야하는 경우 핸들러를 타이머에 등록하고 해당 핸들러의 모든 항목을 올바른 순서로 호출하십시오.

의견에서 언급했듯이 Windows는 실제로 고성능 실시간 OS로 설계되지 않았습니다. 그러나 99.99 %의 데스크톱 응용 프로그램에는 이것이 꼭 필요한 것은 아닙니다 (따라서 Microsoft의 디자인 결정, 주로 데스크톱 OS).

+2

QPC 메서드는 StopWatch 클래스를 통해 .NET에서 제공됩니다. 그러나 궁극적으로 Windows는 사용자 응용 프로그램을위한 "실시간"운영 체제가되도록 설계되지 않았습니다. 정확한 서브 밀리 초 타이밍은 어렵습니다. –

+0

StopWatch 노트에 감사 드리며 Windows에 관한 중요한 점입니다. 그에 따라 내 대답을 업데이트했습니다. –

+0

설명 해 주셔서 감사합니다. 나는 사건이 다른 순서로 시작되는 원인에 관해서 타이밍에 관심이 없다. Russell의 설명에서 벗어난 가장 좋은 점은 하나의 이벤트 처리기를 등록하여 내 코드가 예상대로 실행되도록 보장하는 것입니다. –

관련 문제