C#每N分钟重置一个递增计数器

时间:2019-01-09 12:37:48

标签: c# .net

我的程序中有一个计数器,希望每10分钟重置为0。

预计我的程序将引发事件。这些事件与警告有关,这是由于资源使用过多或在我们的实验中超出测试场景而导致的,这将需要采取某些措施,例如以CSV格式转储一些与测试相关的数据以及Tiff格式的某些图。当事件计数器达到3时,将每1分钟生成一次Tiff文件。

但是由于Tiff文件的大小,我希望不要过度生产这些文件。因此,重置计数器将确保仅跟踪重复发生的事件以采取进一步的措施。

在不添加过多不必要细节的情况下,我的程序的主要结构如下:

class Program
{
    static void Main(string[] args)
    {
      counter = 0;

      using (an API)
      {
            // do something here, which may raise an event

            while (event)
            {
                // take an action

                counter++;  // keeps track of events raised
            }

            if (counter > 3)
            {
                // take a specific action
            }
            else
            {
                // take action B
            }

            counter = 0;    // reset counter every 10 minutes, by calling a timer or async method

            // to keep the application going
            System.Windows.Forms.Application.Run();
      }
    }

    // a timer method() // to help me reset the counter
    // or
    // an Async method ResetCounter()
}

我试图通过创建计时器方法来启动计时器:

private static bool TimeCounter() 
{ 
    System.Timers.Timer _delayTimer = new System.Timers.Timer(); 
    _delayTimer.Interval = 100000;   
    _delayTimer.Enabled = true;
    // _delayTimer.Elapsed += new ElapsedEventHandler(_delayTimer_Elapsed); // attempted to use an additional method as well that could get triggered after 10 mins but it gets too complicated
    _delayTimer.Start();
    // not sure how to set this timer to return a boolean true when time elapsed, rather than calling another method
    _delayTimer.AutoReset = autoreset; 
}

private static void _delayTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
 { 
     ResetCounter(); // the method not created yet
 } 

但是,采用计时器方法,首先,我不确定如何使计时器在时间流逝时返回布尔值true, 其次,如果我的API在main方法中每次在程序经过if-else条件时都调用此方法,则这可能会再次重置计时器,而不会让前10分钟的计时器过去。

因此遇到异步等待,我觉得这对我来说可能是一个更好的选择,我可能会打电话给我(在Stackoverflow上见过这样的东西)来重置计数器?:

var result = Task.Run(async () => { return await myAsyncMethod(); }).Result;

我以前从未使用过async-await,因此不确定如何使用async-await来达到预期的结果。

4 个答案:

答案 0 :(得分:3)

我只会和DateTime.Now

合作

1)每当您重置计时器或第一次执行代码时,都要保存当前时间:

var lastReset = DateTime.Now;

2)检查lastReset是否在10分钟或更早之前:

if(lastReset.AddMinutes(10) <= DateTime.Now)
{
    counter = 0;
}

答案 1 :(得分:1)

如果您希望在10分钟后立即重置计数器,而不管此时发生了什么事情,那么您可以继续使用System.Timers.Timer的想法。您对问题的评论表明这就是您想要的。

要使某个计时器到期时发生某些事情,请为Elapsed事件附加一个事件处理程序。我建议使用lambda表达式将处理程序创建为匿名函数,如下所示:

_delayTimer.Elapsed += (o,e) => { counter = 0; };

由于此代码引用了counter,因此它必须位于counter可用的位置。 (new ElapsedEventHandler部分是不必要的,因为您将附加到事件,所以编译器会自动为您创建委托。)

使用object initializer syntax使代码更整洁,创建和配置Timer即可:

var delayTimer = new System.Timers.Timer
{
    Interval = 600000, // 10 minutes is 10 * 60 * 1000 == 600000 ms 
    AutoReset = true, // makes the timer start over automatically
};
delayTimer.Elapsed += ((o, e) => { counter = 0; });
delayTimer.Start();

请注意,无需显式设置Timer的Enabled属性,因为Start()方法将为您完成此操作。

旁注:一件很酷的事情是,实际上声明counter的位置无关紧要(只要在创建处理程序时可用)。这种匿名函数引用“外部”变量的结构会导致counter上的“关闭”。在C#中,闭包使变量“共享”,这样,即使从声明该变量的作用域之外的位置调用该函数,该函数也可以访问该变量。换句话说,即使counter是局部变量(尽管出于其他原因,这可能是不切实际的),该方法仍然有效。

完整示例(控制台应用程序)

using System;
using System.Timers;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            // Declare the counter somewhere
            var counter = 0;

            // Create timer
            var delayTimer = new Timer
            {
                Interval = 5000, // Changed to 5 seconds for demo purposes 
                AutoReset = true,
            };

            // Create the event handler
            delayTimer.Elapsed += ((o, e) =>
            {
                // Print counter value before resetting
                Console.WriteLine($"[Timer elapsed] The counter has value {counter}, resetting it...");
                counter = 0;
            });

            // Start the timer
            delayTimer.Start();

            // Now simulate doing other stuff while the timer is running...
            Console.WriteLine("I'll be silently incrementing the counter at a random pace.");
            Console.WriteLine("Every five seconds, the timer event handler will reset the counter " +
                              "right after telling you how far I got.");
            var r = new Random();
            while (true)
            {
                // Sleep for a random number of milliseconds (between 0 and 999)
                var sleepLength = r.Next() % 1000;
                System.Threading.Thread.Sleep(sleepLength);

                // Increment the counter
                counter++;
            }

            // Console output example (values will be random): 

            // I'll be silently incrementing the counter at a random pace.
            // Every five seconds, the timer event handler will reset the counter right after telling you how far I got.
            // [Timer elapsed] The counter has value 11, resetting it...
            // [Timer elapsed] The counter has value 9, resetting it...
            // [Timer elapsed] The counter has value 12, resetting it...
            // [Timer elapsed] The counter has value 10, resetting it...
            // [Timer elapsed] The counter has value 9, resetting it...
            // [Timer elapsed] The counter has value 8, resetting it...
            // [Timer elapsed] The counter has value 6, resetting it...
            // [Timer elapsed] The counter has value 4, resetting it...
            // [Timer elapsed] The counter has value 14, resetting it...
        }
    }
}

答案 2 :(得分:0)

另一种解决方案是使用Thread.Timer,如果需要精确的间隔,请使用@speschel建议的逻辑

static int counter = 0;
        static void Main(string[] args)
        {

            Timer timer = new Timer(ResetCount, null, 0, 100000);
            while (true)
            {               
                //Event simulation
                if (Console.ReadKey().Key == ConsoleKey.Enter)
                {
                    Console.WriteLine("Event triggerd"); ;
                    counter++;
                }

                if (counter > 3)
                {
                    Console.WriteLine("Take a specific action");
                }
                else
                {
                    Console.WriteLine("Take action B");
                }

                Thread.Sleep(1000);
            }           
        }

        private static void ResetCount(object state)
        {
            counter = 0;
        }

答案 3 :(得分:0)

real 问题在注释中:

  

这些事件与警告有关,这些警告是由于资源占用过多或实验中的测试场景不可用而引起的,这将需要采取某些措施,例如以CSV格式转储一些与测试相关的数据以及Tiff格式的某些图。当事件计数器达到3时,将每1分钟生成一次Tiff文件。

     

但是由于Tiff文件的大小,我希望不要过度生产这些文件。因此,重置计数器将确保仅跟踪重复发生的事件以采取进一步的措施。

处理事件流是Reactive Extensions的领域,可以作为NuGet包使用。 Rx处理事件流的方式与LINQ处理数据的方式相同。

假设我们有一个事件源,那么如果此代码每3分钟有10个以上的警告,则可以执行一项操作:

        var warnings = eventSource
                .Where(evt => evt.Level >= Level.Warning)
                .Buffer(TimeSpan.FromMinutes(3))
                .Where(evts => evts.Count > 10)
                .Subscribe(evts => ProcessEvents(evts));

Buffer在3分钟的窗口中批量处理事件。 Where()像LINQ的Where一样过滤事件,并且只允许警告级别或更高的事件。以后,它仅允许具有10个以上警告的批次。

最终结果是仅在3分钟的窗口中有10个以上的警告时才调用ProcessEvents

源是实现IObservable的任何类。事件,任务或数据等可以转换为Observable。不管警告的来源是什么,如果它可以实现IObservable接口,则可以与Rx一起使用。

在最简单的情况下,Subject可用于实现简单的可观察对象,该对象仅在有人调用其OnNext方法时才产生事件。人们通常对此不屑一顾,因为它有点像在需要LINQ查询时使用for循环,但是它对演示如何使用Rx很有用:

var eventSource=new Subject<TestEvent>();

//Somewhere where warnings are raise 
eventSource.OnNext(new TestEvent { Level = Level.Info });
...
eventSource.OnNext(new TestEvent { Level = Level.Warning });

Rx库提供了transform data, events, tasks等可观察对象的方法。例如,FromEventPattern会将.NET模式转换为IObservable:

var eventSource= Observable.FromEventPattern(h => someClass.SomeEvent += h,
                                             h => someClass.someEvent -= h);