如何在两个计时器之间创建延迟?

Posted

技术标签:

【中文标题】如何在两个计时器之间创建延迟?【英文标题】: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 会的。 【参考方案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 的异步方法。它有效,但冻结了屏幕和首选计时器。

以上是关于如何在两个计时器之间创建延迟?的主要内容,如果未能解决你的问题,请参考以下文章

启用外设定时器和定时器实际开始计数之间是不是有延迟?这是啥延迟,是啥原因造成的?

swift 3:手表应用:如果手表进入睡眠状态,界面控制器之间会有延迟

键控函数之间的 Jquery 延迟

Vertx 定时器更改延迟

在 Swift 中休眠或延迟 Timer 线程

C# Xamarin - 如何延迟第二个 void