C# 定期触发函数的正确方法
C# Proper way of periodically firing functions
我有大约 20 个函数,其中一些应该每秒触发一次,一些应该每秒触发 20 次,其他的一分钟触发两次目前我有这样的东西:
DateTime nowVar = DateTime.Now;
var lastExecutionFunc1 = nowVar;
var lastExecutionFunc2 = nowVar;
var lastExecutionFunc3 = nowVar;
var lastExecutionFunc4 = nowVar;
DateTime _now = DateTime.Now;
if ((_now - lastExecutionFunc1).TotalSeconds >= 0.1)
{
lastExecutionFunc1 = _now;
//dosomething
}
if ((_now - lastExecutionFunc2).TotalSeconds >= 0.5)
{
lastExecutionFunc2 = _now;
//do something else
}
if ((_now - lastExecutionFunc3).TotalSeconds >= 30)
{
lastExecutionFunc3 = _now;
//do something else
}
.....
虽然这可行,但我不禁认为应该有一种更优雅的方法来做到这一点。创建一个 var 来存储每次执行会使核心看起来非常混乱。我猜我可以使用 pair,但这也不太好。
有什么建议吗?
编辑:如果您想了解我正在努力完成的工作,you can see the whole code here。在第 525 行。
你是否为这个定时事件写了一个无限循环?非常糟糕的做法。
如果你们中的任何一个 "do something" 持续很长时间,所有其他功能(和主程序)将停止工作。如果你的函数可以独立工作,就让它们独立工作,并为每个函数使用不同的计时器。如果某些功能是独占的,请使用信号量(静态成员)以避免并行工作。
这是一种方法:
我创建了一个 class,它使用 Action
委托将方法与其间隔绑定:
public class ActionInvoker
{
private DateTime _LastRunTime;
private Action _Action;
private double _Interval;
public ActionInvoker(Action action, double interval)
{
_Action = action;
_Interval = interval;
_LastRunTime = DateTime.Now;
}
public void InvokeAction()
{
var now = DateTime.Now;
if ((now - _LastRunTime).TotalMilliseconds >= _Interval)
{
_LastRunTime = now;
_Action.Invoke();
}
}
我添加了 ActionInvoker
的列表作为表单的私有成员:
private List<ActionInvoker> _Actions;
现在,在窗体的构造函数中,我已经这样做了:
_Actions = new List<ActionInvoker>();
_Actions.Add(new ActionInvoker(DoThis, 10000));
_Actions.Add(new ActionInvoker(DoThat, 3000));
现在我在 Timer.Tick 事件处理程序中只有这段代码:
foreach(var ai in _Actions)
{
ai.InvokeAction();
}
当然,这只是一个基本的 POC,如果您想向方法发送参数,或者如果您的方法 return 值,则需要进一步开发它。
另请注意,您最好 运行 这些方法 async
以防止 UI 在完成之前冻结。
下面的代码展示了一个带有同步锁的基本调度程序。
using System;
using System.Collections.Generic;
using System.Timers;
namespace TimerUsage
{
class Program
{
static void Main(string[] args)
{
Scheduler scheduler = new Scheduler();
scheduler.ScheduleMethod(100, () => Console.WriteLine("func1"));
scheduler.ScheduleMethod(200, () => Console.WriteLine("func2"));
scheduler.ScheduleMethod(300, () => Console.WriteLine("func3"));
scheduler.ScheduleMethod(1000, () => Console.WriteLine("func4"));
scheduler.Run();
System.Threading.Thread.Sleep(10000);
}
}
public class Scheduler
{
private Dictionary<int, Row> _schedule = new Dictionary<int, Row>();
public void ScheduleMethod(int interval, Action method)
{
Row row;
if (!_schedule.TryGetValue(interval, out row))
{
row = new Row(interval);
_schedule[interval] = row;
}
row.AddMethod(method);
}
public void Run()
{
foreach (var item in _schedule)
{
item.Value.StartTimer();
}
}
}
internal class Row
{
private object _syncLock = new object();
private Timer _timer;
private List<Action> _methods = new List<Action>();
public Row(int interval)
{
_timer = new System.Timers.Timer(interval);
_timer.Elapsed += ExecuteItems;
}
private void ExecuteItems(object sender, ElapsedEventArgs e)
{
lock (_syncLock)
{
foreach (var method in _methods)
{
method();
}
}
}
public void AddMethod(Action method)
{
_methods.Add(method);
}
public void StartTimer()
{
_timer.Start();
}
}
}
我有大约 20 个函数,其中一些应该每秒触发一次,一些应该每秒触发 20 次,其他的一分钟触发两次目前我有这样的东西:
DateTime nowVar = DateTime.Now;
var lastExecutionFunc1 = nowVar;
var lastExecutionFunc2 = nowVar;
var lastExecutionFunc3 = nowVar;
var lastExecutionFunc4 = nowVar;
DateTime _now = DateTime.Now;
if ((_now - lastExecutionFunc1).TotalSeconds >= 0.1)
{
lastExecutionFunc1 = _now;
//dosomething
}
if ((_now - lastExecutionFunc2).TotalSeconds >= 0.5)
{
lastExecutionFunc2 = _now;
//do something else
}
if ((_now - lastExecutionFunc3).TotalSeconds >= 30)
{
lastExecutionFunc3 = _now;
//do something else
}
.....
虽然这可行,但我不禁认为应该有一种更优雅的方法来做到这一点。创建一个 var 来存储每次执行会使核心看起来非常混乱。我猜我可以使用 pair,但这也不太好。
有什么建议吗?
编辑:如果您想了解我正在努力完成的工作,you can see the whole code here。在第 525 行。
你是否为这个定时事件写了一个无限循环?非常糟糕的做法。 如果你们中的任何一个 "do something" 持续很长时间,所有其他功能(和主程序)将停止工作。如果你的函数可以独立工作,就让它们独立工作,并为每个函数使用不同的计时器。如果某些功能是独占的,请使用信号量(静态成员)以避免并行工作。
这是一种方法:
我创建了一个 class,它使用 Action
委托将方法与其间隔绑定:
public class ActionInvoker
{
private DateTime _LastRunTime;
private Action _Action;
private double _Interval;
public ActionInvoker(Action action, double interval)
{
_Action = action;
_Interval = interval;
_LastRunTime = DateTime.Now;
}
public void InvokeAction()
{
var now = DateTime.Now;
if ((now - _LastRunTime).TotalMilliseconds >= _Interval)
{
_LastRunTime = now;
_Action.Invoke();
}
}
我添加了 ActionInvoker
的列表作为表单的私有成员:
private List<ActionInvoker> _Actions;
现在,在窗体的构造函数中,我已经这样做了:
_Actions = new List<ActionInvoker>();
_Actions.Add(new ActionInvoker(DoThis, 10000));
_Actions.Add(new ActionInvoker(DoThat, 3000));
现在我在 Timer.Tick 事件处理程序中只有这段代码:
foreach(var ai in _Actions)
{
ai.InvokeAction();
}
当然,这只是一个基本的 POC,如果您想向方法发送参数,或者如果您的方法 return 值,则需要进一步开发它。
另请注意,您最好 运行 这些方法 async
以防止 UI 在完成之前冻结。
下面的代码展示了一个带有同步锁的基本调度程序。
using System;
using System.Collections.Generic;
using System.Timers;
namespace TimerUsage
{
class Program
{
static void Main(string[] args)
{
Scheduler scheduler = new Scheduler();
scheduler.ScheduleMethod(100, () => Console.WriteLine("func1"));
scheduler.ScheduleMethod(200, () => Console.WriteLine("func2"));
scheduler.ScheduleMethod(300, () => Console.WriteLine("func3"));
scheduler.ScheduleMethod(1000, () => Console.WriteLine("func4"));
scheduler.Run();
System.Threading.Thread.Sleep(10000);
}
}
public class Scheduler
{
private Dictionary<int, Row> _schedule = new Dictionary<int, Row>();
public void ScheduleMethod(int interval, Action method)
{
Row row;
if (!_schedule.TryGetValue(interval, out row))
{
row = new Row(interval);
_schedule[interval] = row;
}
row.AddMethod(method);
}
public void Run()
{
foreach (var item in _schedule)
{
item.Value.StartTimer();
}
}
}
internal class Row
{
private object _syncLock = new object();
private Timer _timer;
private List<Action> _methods = new List<Action>();
public Row(int interval)
{
_timer = new System.Timers.Timer(interval);
_timer.Elapsed += ExecuteItems;
}
private void ExecuteItems(object sender, ElapsedEventArgs e)
{
lock (_syncLock)
{
foreach (var method in _methods)
{
method();
}
}
}
public void AddMethod(Action method)
{
_methods.Add(method);
}
public void StartTimer()
{
_timer.Start();
}
}
}