2009-11-10 51 views
1

我一直在阅读Daniel Solis的Illustrated C# 2008(一本优秀的书,顺便说一句),并决定花更多时间在活动上来进一步理解这个话题。我试图理解为什么每次运行该程序时都会看到不同的结果,以及我可以从中学到什么。这些Timer事件为什么会在不一致的时候产生?

(源代码如下)在书中的示例代码中,有一个MyTimerClass,它有一个订阅System.Timers.Timer的事件。还有两个其他类,ClassA和ClassB,它们都具有写入控制台的事件处理程序(一个是静态的,另一个不是)。在程序的main函数中,这些类的事件处理程序绑定到MyTimerClass的实例中的事件。另一个函数通过lambda表达式添加到事件成员中。

使用作者的代码后,我决定添加另一个类,ClassC。我不是在程序的Main函数中添加事件处理程序,而是决定在ClassC的构造函数中创建一个单独的MyTimerClass对象,然后订阅MyTimerClass的事件。

当我在3个不同的场合运行我的代码4.25秒时,我的结果总是以不同的顺序。订单的Main函数的事件总是以相同的顺序调用ClassA,ClassB,然后是Lambda。但是,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定时器的工作方式。”你在这里没有做错任何事,你看到的结果是正常的行为。

Microsoft在Windows中提供的本地计时器不能保证在其滴答时间内准确无误。如果您设置一个计时器以每1000毫秒为单位进行计时,则可以保证至少为至少为 1000毫秒,但不是恰好为 1000毫秒。它通常会非常接近,有时甚至是确切的,但如果您需要非常高的精度时间,则需要查看其他机制。在Win32 C/C++ API级别,适当的机制是QueryPerformanceFrequency/QueryPerformanceCounter方法。在.NET中,您可以使用StopWatch类来获得此功能。对于这个命令,如果你在.NET中附加了多个处理程序到一个计时器,我敢肯定,也不能保证它们将以任何给定的顺序执行。我不知道到底是什么算法微软引擎盖下使用解雇他们,但你的结果清楚地表明,它并不保证他们是为了一个算法。如果您需要的代码是为了,然后注册一个处理为定时器和从该处理程序调用的一切,以正确的顺序。

正如在评论中指出,Windows未真正设计为一个高性能的实时操作系统。然而,这不是桌面应用程序的99.99%,确有必要(因此微软的设计决策,它是,毕竟主要是桌面操作系统)。

+2

的QPC方法通过秒表类暴露在.NET。但最终,Windows不是为用户应用程序设计的“实时”操作系统;准确的亚毫秒时间是很难做到的。 – 2009-11-10 04:58:14

+0

感谢StopWatch笔记,以及关于Windows的优秀点。我相应地更新了我的答案。 – 2009-11-10 05:10:10

+0

谢谢你的解释。我并不担心时间,因为我是关于什么导致事件以不同的顺序开火。我从罗素的解释抢走了最好的事情是让我的代码是在保证我希望为了执行注册一个事件处理程序。 – 2009-11-10 05:48:11

相关问题