一种极简的异步超时处理机制设计与实现(C#版) (2)

以每秒进行一次检测的粒度运行,使用 System.Timers.Timer 非常合适,它的职能是判断运行时间到达与否决定是否将任务移至执行队列。

// 超时检测者,每秒扫描是否达到超时,超时则加入超时任务队列 private System.Timers.Timer _TimeoutChecker = new System.Timers.Timer(); // 超时检测者 _TimeoutChecker.Interval = 1000; _TimeoutChecker.Elapsed += new System.Timers.ElapsedEventHandler(CheckTimerTick); _TimeoutChecker.Start(); /// <summary> /// 超时任务检测者 /// 对于,时间已经超过了设定的超时时间的,加入超时任务执行队列 /// </summary> /// <param></param> /// <param></param> private void CheckTimerTick(object sender, System.Timers.ElapsedEventArgs e) { long secondTicks = DateTime.Now.Ticks / 10000000; // 遍历,把时间已到达超过超时时间的找出来 lock (_DictionaryLocker) { foreach (var key in _TaskIdDictionary.Keys.ToList()) { var task = _TaskIdDictionary[key]; if (_TaskIdDictionary[key].ExecuteSecondTicks <= secondTicks) { // 加入超时任务执行队列,并移除清单 lock (_RunLocker) { _TaskRunQueue.Enqueue(task); RemoveTimeoutTask(task.TaskId); } // 有生产,则通知执行线程(消费者) _WaitHandle.Set(); } } } } 3.6任务执行者

执行队列中存在任务时就执行,否则等待。线程等待,这里使用了 EventWaitHandle,EventWaitHandle.WaitOne 等待,生产者使用 EventWaitHandle.Set 方法进行通知,配合起来有效地运行队列中的任务。

// 超时任务执行线程 private Thread _TaskRunThread; // 用于同步操作任务队列的线程信号(生产者,消费者通知作用) private EventWaitHandle _WaitHandle = new AutoResetEvent(false); // 用于退出执行线程的一个标识 private bool _Working = true; /// <summary> /// 超时任务执行线程主体 /// </summary> private void TaskRunning() { while (_Working) { TimeoutTask<T> task = null; lock (_RunLocker) { if (_TaskRunQueue.Count > 0) { task = _TaskRunQueue.Dequeue(); } } // 存在超时任务执行其回调 if (task != null) { task.Callback(task.ObjectKey); } else { // 等待生产者通知 _WaitHandle.WaitOne(); } } } 3.7向外开放的接口

代码如是说:

/// <summary> /// 指定对象标识,超时时长(秒为单位),超时执行回调,加入到超时检测字典中 /// </summary> /// <param></param> /// <param></param> /// <param></param> /// <returns></returns> public long AddTimeoutTask(T objectKey, int timeoutSeconds, TimeoutCallback<T> callback) { TimeoutTask<T> task = new TimeoutTask<T>(); task.ObjectKey = objectKey; task.TimeoutSeconds = timeoutSeconds; task.Callback = callback; long taskId = GetNextTaskId(); task.TaskId = taskId; task.ExecuteSecondTicks = DateTime.Now.Ticks / 10000000 + timeoutSeconds; lock (_DictionaryLocker) { // 以任务标识为主键的任务清单 _TaskIdDictionary[taskId] = task; // 以对象标识为主键的任务清单 if (_TaskObjectKeyDictionary.ContainsKey(objectKey)) { _TaskObjectKeyDictionary[objectKey].Add(task); } else { List<TimeoutTask<T>> list = new List<TimeoutTask<T>>(); list.Add(task); _TaskObjectKeyDictionary[objectKey] = list; } } return taskId; } /// <summary> /// 根据对象标识移除超时任务设置 /// </summary> /// <param></param> public void RemoveTimeoutTask(T objectKey) { lock (_DictionaryLocker) { if (_TaskObjectKeyDictionary.ContainsKey(objectKey)) { // 在任务标识为主键的清单中移除相应的该对象的多个超时任务 foreach (var task in _TaskObjectKeyDictionary[objectKey]) { _TaskIdDictionary.Remove(task.TaskId); } _TaskObjectKeyDictionary[objectKey].Clear(); } } } /// <summary> /// 根据任务标识移除超时任务设置 /// </summary> /// <param></param> public void RemoveTimeoutTask(long taskId) { lock (_DictionaryLocker) { if (_TaskIdDictionary.ContainsKey(taskId)) { var task = _TaskIdDictionary[taskId]; _TaskIdDictionary.Remove(taskId); // 在对象标识为主键的清单移除相应的超时任务 _TaskObjectKeyDictionary[task.ObjectKey].Remove(task); } } } 4.应用示例

定义回调处理方法,添加一个超时任务只需要指定简单的参数即可,如下示例,会按什么顺序输出什么呢?

class Program { static void Main(string[] args) { TS.Task.TimeoutTaskRunner<string> runner = new TS.Task.TimeoutTaskRunner<string>(); TS.Task.TimeoutCallback<string> callback = (string key) => { Console.WriteLine(key + " is timeout."); }; runner.AddTimeoutTask("a", 4, callback); runner.AddTimeoutTask("b", 3, callback); runner.AddTimeoutTask("c", 2, callback); Console.ReadKey(); runner.Dispose(); } }

运行结果:

一种极简的异步超时处理机制设计与实现(C#版)

5.小结

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfdzs.html