如何在两个计时器之间创建延迟?
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 的异步方法。它有效,但冻结了屏幕和首选计时器。以上是关于如何在两个计时器之间创建延迟?的主要内容,如果未能解决你的问题,请参考以下文章
启用外设定时器和定时器实际开始计数之间是不是有延迟?这是啥延迟,是啥原因造成的?