C#有三种计时器,一个只能在winform 之类的窗口程序之中用,后台能用的只剩下了System.Timers.Timer和System.Threading.Timer, 但这两个计时器都不完备,我们业务中遇到的一些特殊需求无法满足,比如同一时间计时回调不能同时执行等。于是,我花了点时间包装了两个新计时器,一个支持任务串行化(计时器委托在同一时间只有一个执行),一个支持固定延迟(保证上次任务执行结束和此次任务执行开始之间时间恒定)。
1. 恒定频率计时器(支持串行化)。 封装的FixedRateTimer类:
C#
/// <summary> /// 频率恒定计时器 /// </summary> public class FixedRateTimer { /// <summary> /// 构造方法 /// </summary> /// <param name="start">启动后延迟多久开始执行</param> /// <param name="interval">下次执行距离这次执行开始的时间</param> /// <param name="action">任务委托</param> /// <param name="state">任务状态参数</param> /// <param name="concurrent">是否允许并行 /// <remarks>如果允许并行,同一时间可能有几个任务同时执行; /// 如果不允许,当新任务发现上次任务正在运行时,将忽略此次任务</remarks> /// </param> public FixedRateTimer(TimeSpan start, TimeSpan interval, TimerCallback action, object state = null, bool concurrent = false) { _action = action; _isConcurrent = concurrent;
_start = start; _interval = interval; _state = state; }
private void TimerAction(object s) { //增加正在执行任务个数 Interlocked.Increment(ref _jobCount); if (!_isConcurrent) { //:任务无法并行执行,所以必须串行化
//当无任务执行(_isRunning==0)时更改状态为运行(_isRunning=0), //然后开始执行新任务,因为代码可能多线程执行,所以用原子操作 if (Interlocked.CompareExchange(ref _isRunning,1,0) == 0) { _action(s); //更改状态为无任务运行 Interlocked.Exchange(ref _isRunning, 0); } } else { //无串行约束,直接执行 _action(s); } //减少正在执行任务个数 Interlocked.Decrement(ref _jobCount); }
/// <summary> /// 开始计时 /// </summary> public void Start() { _isRunning = 0; _timer = new Timer(TimerAction, _state, _start, _interval); }
/// <summary> /// 结束计时 /// </summary> public void Stop() { _timer.Dispose(); WaitUtilFinish(); _timer = null; _isRunning = 0; }
/// <summary> /// 等待已经开始执行的任务执行完 /// </summary> private void WaitUtilFinish() { while (_jobCount != 0) { Thread.Sleep(_interval); } }
//用来在不允许并发时保持任务串行 private int _isRunning = 0; private int _jobCount = 0; private bool _isConcurrent = false;
private TimerCallback _action = null; private Timer _timer = null;
private TimeSpan _start; private TimeSpan _interval; private Object _state; }
测试代码:
C#
1 FixedRateTimer timer = new FixedRateTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), 2 s => 3 { 4 Console.WriteLine("开始运行:" + DateTime.Now); 5 Thread.Sleep(5000); 6 Console.WriteLine("结束运行:" + DateTime.Now); 7 }, null); 8 timer.Start(); 9 Thread.Sleep(TimeSpan.FromSeconds(20)); 10 timer.Stop(); 11 Console.WriteLine("再试一次"); 12 timer.Start(); 13 Thread.Sleep(TimeSpan.FromSeconds(20)); 14 timer.Stop();
运行结果:
可以看到,虽然任务执行时间很长,但仍然是先后一个挨着一个执行的。
2. 恒定延迟计时器(任务间空闲时间恒定)。 封装的FixedDelayTimer类:
C#
public class FixedDelayTimer { /// <summary> /// 构造方法 /// </summary> /// <param name="start">启动后延迟多久开始执行</param> /// <param name="interval">下次执行开始距离这次执行结束的时间</param> /// <param name="action">任务委托</param> /// <param name="state">任务状态参数</param> public FixedDelayTimer(TimeSpan start, TimeSpan interval, TimerCallback action, object state = null) { _action = action;
_start = start; _interval = interval; _state = state; }
private void TimerAction(object s) { Thread.Sleep(_start); while (_isExit == 0) { _action(s); Thread.Sleep(_interval); } }
/// <summary> /// 开始计时 /// </summary> public void Start() { _isExit = 0; _thread = new Thread(new ParameterizedThreadStart(TimerAction)); _thread.Start(); }
/// <summary> /// 结束计时 /// </summary> public void Stop() { //通知线程退出 Interlocked.Exchange(ref _isExit, 1); //等待线程执行完毕 _thread.Join(); _thread = null; }
private int _isExit = 0;//是否退出计时器循环
private TimerCallback _action = null; private Thread _thread = null;
private TimeSpan _start; private TimeSpan _interval; private Object _state; }
测试代码:
C#
1 FixedDelayTimer timer = new FixedDelayTimer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), 2 s => 3 { 4 Console.WriteLine("开始运行:" + DateTime.Now); 5 Thread.Sleep(5000); 6 Console.WriteLine("结束运行:" + DateTime.Now); 7 }, null); 8 timer.Start(); 9 Thread.Sleep(TimeSpan.FromSeconds(20)); 10 timer.Stop(); 11 Console.WriteLine("再试一次"); 12 timer.Start(); 13 Thread.Sleep(TimeSpan.FromSeconds(20)); 14 timer.Stop();
运行结果:
可以看到,每次任务执行完的时间距离下次任务开始执行的时间恒定为我们指定的值。
|
请发表评论