【问题标题】:How to create delay between two timers?如何在两个计时器之间创建延迟?
【发布时间】:2020-07-15 09:05:38
【问题描述】:

目标

有两种方法:Add() 和 Remove()。

  • Add() 方法每 X 秒发生一次,直到被用户停止;添加 反对DB;等待 N 秒;调用 Remove();

  • Remove() 方法由 Add() 方法调用并删除由 Add() 方法添加的对象 Add() 方法。

我的代码

        static bool keepGoing = true;

        static System.Timers.Timer AddMethodTimer= new System.Timers.Timer();
        static System.Timers.Timer RemoveMethodTimer = new System.Timers.Timer();

        private static void Execute()
        {
            if (!keepGoing)
            {
                AddMethodTimer.Stop();
                RemoveMethodTimer.Stop();
            }
            else
            {
                AddMethodTimer.Interval = 30; // X = 30
                RemoveMethodTimer.Interval = 5; // N = 5

                AddMethodTimer.Elapsed += new ElapsedEventHandler(Add);
                AddMethodTimer.Start();
                Thread.Sleep(RemoveMethodTimer.Interval)//In this case, N=5; 
                RemoveMethodTimer.Elapsed += new ElapsedEventHandler(Remove); 
            }
        }
        private static void Add(object source, ElapseEventArgs e)
        {
             //add operation
        }
        private static void Remove(object source, ElapseEventArgs e)
        {
            //remove operation
        }

用户只能更改“keepGoing”变量。如果它是假的,计时器应该停止。整个代码在更改时触发(我有一个处理它的帖子 IActionResult。它工作正常。当 keepGoing 更改时,它会输入我提供的代码)。

注意 : 如果对象被插入到 DB 并且用户使 keepGoing 为 false,Remove() 将不会被执行

【问题讨论】:

  • 重用同一个定时器,设置第一个经过的事件的时间间隔。更好的是使用异步。 await Task.Delay(30); AddMethodTimer(); await Task.Delay(5); RemoveMethodTimer()
  • @Fildor 我的坏人会编辑它。第一个“添加”称为 Execute
  • 1 问题:如果对象已插入数据库但尚未删除,而现在用户将“keepGoing”切换为 false,该怎么办。 1.Remove是否会如期执行? 2.Remove立即执行?或者3.Remove根本不被执行?
  • 如果要异步,则不需要计时器。
  • @Fildor 会的。

标签: c# asp.net-core .net-core system.timers.timer


【解决方案1】:

您可以尝试异步方式。这不会在您的代码中“使用”计时器。通过使用 CancellationTokenSource,您也可以取消 Task.Delays。

这是一个例子。你应该改进它,因为如果你在 30 秒的延迟内取消它,它仍然会调用 Add 和 Remove。您可以为此使用if (!tcs.IsCancellationRequested) Add();。随便玩玩吧。

static CancellationTokenSource tcs = new CancellationTokenSource();

private static async Task ExecuteAsync()
{
    while (!tcs.IsCancellationRequested)
    {
        await Task.Delay(30000, tcs.Token);

        Add();

        await Task.Delay(5000, tcs.Token);

        Remove();
    }
}

private static void Stop()
{
    tcs.Cancel();
}

private static void Add()
{
    //add operation
}

private static void Remove()
{
    //remove operation
}

【讨论】:

    【解决方案2】:

    坚持使用计时器,我建议这样:

    private static System.Timers.Timer myTimer = null;
    private static readonly syncObj = new object();
    
    public static bool Execute( bool runTimer )
    {
         if(runTimer)
         {
             lock(syncObj) // myTimer access shall be thread safe
             {
                 if(myTimer != null) return false;
                 // Timer is not active => activate
                 myTimer = new System.Timers.Timer();
                 myTimer.AutoReset = false; // 1-Time trigger!!
                 myTimer.Elapsed += AddOnTimer;
                 myTimer.Interval = TimeSpan.FromSeconds(30).TotalMilliseconds; // Interval is in ms!
                 myTimer.Enabled = true;
             }
         }
         else
         {
            lock(syncObj)
            {
                if( myTimer == null ) return false;
                myTimer.Enabled = false;
                myTimer.Dispose();
                myTimer = null;
            }
         }
         return true;
    }
    
    private static void AddOnTimer(object sender, ElapsedEventArgs e)
    {
        AddObjToDB();
        lock( syncObj )
        {
          if( myTimer == null ) return; // myTimer has been canceled, meanwhile
          myTimer.Elapsed -= AddOnTimer;    // Instead of Add, next time
          myTimer.Elapsed += RemoveOnTimer; // execute Remove
          myTimer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;            
          myTimer.Enabled = true;
        }
    }
    
    private static void RemoveOnTimer(object sender, ElapsedEventArgs e)
    {
        RemoveObjFromDB();
        lock( syncObj )
        {
          if( myTimer == null ) return; // myTimer has been canceled
          myTimer.Elapsed -= RemoveOnTimer;  // Instead of Remove, next time
          myTimer.Elapsed += AddOnTimer;     // execute Add
          myTimer.Interval = TimeSpan.FromSeconds(30).TotalMilliseconds;
          myTimer.Enabled = true;
        }
    }
    

    异步方法:

    public static async Task Execute( CancellationToken cancel )
    {
        while( !cancel.IsCancellationRequested )
        {
          await Task.Delay(TimeSpan.FromSeconds(30), cancel);
          await AddObjToDBAsync(cancel);
          await Task.Delay(TimeSpan.FromSeconds(5), cancel);
          await RemoveFromDBAsync(cancel);
        }
    }
    
    private static async Task AddObjToDBAsync( CancellationToken cancel )
    { 
       if( !cancel.IsCancellationRequested )
       {
           await YourDataLayer.AddAsync( someObj ); // <= I made this up, of course. "Async all the way" is recommended.
       }
    }
    
    private static async Task RemoveObjFromDBAsync( CancellationToken cancel )
    { 
       if( !cancel.IsCancellationRequested )
       {
           await YourDataLayer.RemoveAsync( someObj );
       }
    }
    

    *(我刚刚看到,Jeroen 已经发布了一个异步方法。无论如何,我会留在这里。)

    【讨论】:

    • 尝试过的计时器方法。启动和停止工作正常。问题是当计时器启动时,它会在几秒钟内添加对象数次,而不是每 30 秒添加一次。我是这样从 Post IActionResult 调用它的 NewTimerApproach.SetActive(true);
    • 已修复。 @Greg 间隔以毫秒为单位。
    • 谢谢!如果有人感兴趣,我也尝试了 Jeroen 的异步方法。它有效,但冻结了屏幕和首选计时器。
    猜你喜欢
    • 1970-01-01
    • 2011-06-25
    • 1970-01-01
    • 1970-01-01
    • 2022-11-29
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多