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

问题描述 投票:1回答:4

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

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

但是由于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分钟计时器过去。

所以遇到async-await我觉得这对我来说可能是一个更好的选择,我可能会调用这样的东西(在Stackoverflow上看到)来重置计数器?:

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

我以前从未使用async-await,因此不确定如何使用async-await实现所需的结果。

c# .net
4个回答
1
投票

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

要在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是一个局部变量(由于其他原因可能不切实际),这也会起作用。

Full example (console app)

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...
        }
    }
}

3
投票

我只想与DateTime.Now合作

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

var lastReset = DateTime.Now;

2)检查lastReset是否是10分钟或更长时间:

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

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;
        }

0
投票

真正的问题是在评论中:

这些事件对应于由于大量使用资源或在我们的实验中退出测试场景而发出的警告,这需要采取一些措施,例如以CSV格式转储一些与测试相关的数据以及一些Tiff格式的图表。当事件计数器达到3时,Tiff文件将每1分钟生成一次。

但是由于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的任何类。事件,任务或数据等可以转换为Observables。无论警告的来源是什么,如果它可以实现IObservable接口,它可以与Rx一起使用。

在最简单的情况下,Subject可用于实现一个简单的observable,只有当有人调用它的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);
© www.soinside.com 2019 - 2024. All rights reserved.