【问题标题】:Identify different timer runs (System.Timers.Timer)识别不同的计时器运行 (System.Timers.Timer)
【发布时间】:2015-06-07 04:22:40
【问题描述】:

所以我有一个计时器,它每秒运行一次。通常只需要几毫秒即可运行,但有时一个计时器运行的执行时间会超过 1 秒。 这本身不是问题,因为 c# 会处理将不同的运行放入不同(或相同)线程等。

但是,如果我想在同时执行 2 次运行时识别调用了某个方法的运行怎么办? 我想在我的控制台中有一个输出,例如:

Run 1: Method "GetThisStuff" called
Run 1: Method "GetOtherStuff" called
Run 2: Method "GetThisStuff" called
Run 1: Method "GetFinalStuff" called
Run 2: Method "GetOtherStuff" called
Run 2: Method "GetFinalStuff" called

如果我有计时器方法

public static void timer_Elapsed(object sender, ElapsedEventArgs e)  {
     GetMainStuff();
     GetMoreMainStuff();

 }

还有一些虚拟方法:

public void GetMainStuff()
{
   GetThisStuff();
   GetOtherStuff();
}

public void GetMoreMainStuff()
{
   GetFinalStuff();
}

我知道有

ElapsedEventArgs.SignalTime

但我不想通过我的应用程序的每个方法(向下几个“级别”)将此作为参数。

我也知道,并非每次新运行都会有一个新线程。

如果我有一个静态记住的 SignalTime,它会在每次运行时被覆盖。

有什么想法吗?

【问题讨论】:

  • 你的断言是错误的,它总是是个问题。只需将 AutoReset 属性设置为 false 并在事件处理程序结束时调用 Start()。
  • 好吧,我知道多线程通常存在(很多)问题,但这目前并没有给我带来任何问题,这可能就是我的意思。;)但我的问题仍然是如何识别不同的run及时重叠,不要让run一个接一个地跑。

标签: c# .net timer


【解决方案1】:

由于System.Timers.Timer 未密封,您可以对其进行扩展以添加 Counter 属性:

public class TimerWithRunCounter : System.Timers.Timer
{
    public int Counter { get; set; }

    public TimerWithRunCounter(int counter, double interval) : base(interval)
    {
        Counter = counter;
    }
}

在计时器回调中将sender 转换为TimerWithRunCounter 并访问并增加计数器:

public static void timer_Elapsed(object sender, ElapsedEventArgs e)
{
    var timer = (TimerWithRunCounter)sender;
    lock (lockObject)
    {
        Console.WriteLine("Run {0}", timer.Counter);
        timer.Counter++;
    }
}

示例用法:

var timer = new TimerWithRunCounter(0, 1000);

【讨论】:

    【解决方案2】:

    您需要方法本身来了解上下文吗?还是只需要在事件处理程序中知道?

    实现这一点的最简单方法是在与事件处理程序相同的类中维护一个计数器:

    private static int _timerCounter;
    
    public static void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        int counter = Interlocked.Increment(ref _timerCounter);
    
        GetMainStuff();
        GetMoreMainStuff();
    }
    

    如果每个被调用的方法都需要知道这个值,那当然可以传值:

    public static void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        int counter = Interlocked.Increment(ref _timerCounter);
    
        GetMainStuff(counter);
        GetMoreMainStuff(counter);
    }
    

    如果您有一个深度调用链,希望在整个上下文中广泛了解计数器,那么您可以继续上述模式,将值向下传递给每个被调用的方法,或者您可以创建一个“调用上下文" 类,您将这些方法的代码以及当前计数器的值放入其中:

    class TimerContext
    {
        private readonly int _timerCount;
        public int TimerCount { get { return _timerCount; } }
    
        public TimerContext(int timerCount)
        {
            _timerCount = timerCount;
        }
    
        public void GetMainStuff()
        {
            GetThisStuff();
            GetOtherStuff();
        }
    
        public void GetMoreMainStuff()
        {
            GetFinalStuff();
        }
    
        // etc.
    }
    
    public static void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        TimerContext context = new TimerContext(Interlocked.Increment(ref _timerCounter));
    
        context.GetMainStuff();
        context.GetMoreMainStuff();
    }
    

    这样,作为TimerContext 类的成员,您调用的每个方法都可以访问计数器。

    这些只是您可以采取的几种不同方法。当然,还有其他选项,但希望以上内容能让您对这些选项的工作方式有所了解(它们都是主题的变体)。

    【讨论】:

    • 带有“调用上下文”类的想法很好,因为我有一个很深的调用链,把这个参数放在 400 个左右的方法中并不好......但它仍然一个问题......,因为我在我的 elapsed-event-handler 中的所有方法都已经有了合理的类,所以把所有这些放在一个上下文类中只会破坏我整齐定义的类和方法。我只是以虚拟方法为例。有什么想法吗?
    • 好的,我可以让我所有的类都继承自上下文类我猜..嗯
    • 我不鼓励仅仅为了共享某些特定数据而继承一个类。如果不知道你为什么想要柜台,就很难给出具体的建议。 IE。您说可以进行重叠的计时器处理,那么为什么确切的顺序很重要呢?如果它很重要,您可以考虑通过在事件处理程序中加锁而不是计数器来进行序列化。最后,请注意调用上下文的重点是允许 every 方法访问计数器。你真的有 400 种方法都需要知道计数器的值吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多