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

当执行某些动作之后,会期待反馈。最终要么是得到了结果,要么就是超时了。当超时发生时,可能是期望得到通知,或是希望能自动重试,等等。于是设计了一种通用的异步超时的处理机制,以期通过简洁易理解的方式完成超时的处理过程。

2.对外接口设计

从使用的角度,调用方期望的是“指定超时时长,时间到自动执行指定过程”,由此可以得出外部的操作接口参数。从功能角度来看,对于未超时的情况,需要提供在超时时长内随时清除超时任务的功能。

2.1操作接口

在这里,我们把设计的机制称里“超时任务运行器”,从外部来看,其接口与功能结构如下:
(1)添加超时任务,带上对象标识用于回调时传参,指定超时时长和超时回调方法即可把超时处理交给超时任务运行器。返回一个任务标识,用于后续可删除该超时任务。
(2)删除超时任务,指定任务标识即可删除。同时,支持清除一个对象的所有超时任务。

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

2.2使用过程

发起异步操作的同时,添加超时任务,在异步操作成功时,删除超时任务。超时则运行器会自动执行超时任务。如下图,灰色部分由运行器完成:

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

3.超时任务运行器设计与实现

首先,时长的精确粒度设定为秒,这表示超时最低可支持秒级(废话)。设计的基本思路为:对于加入的超时任务,运行器建立清单,并以秒为单位对清单中的任务进行检测,对于时间已经到达或超过的将其移动至超时任务执行队列中,由独立的超时任务运行线程来执行队列中的任务。这里,移动任务至执行队列的检测者谓之“生产者”,任务执行线程谓之“消费者”。

3.1基本结构

运行器维护一个超时任务清单和一个执行队列,一个超时检测者,它使用定时器检测任务是否超时并将超时的加入执行队列,一个任务执行者负责运行超时任务中的回调过程。

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

3.2数据结构

超时任务信息,除了调用者传递的对象标识、超时时长与回调方法,还包括其它运行过程中所需的属性:任务标识、运行时间点。详细信息看如下类定义:

/// <summary> /// 超时回调的委托 /// </summary> /// <typeparam></typeparam> /// <param></param> public delegate void TimeoutCallback<T>(T objectKey); /// <summary> /// 超时任务信息 /// </summary> /// <typeparam></typeparam> public class TimeoutTask<T> { // 任务标识(由超时任务运行器自动分配) public long TaskId { get; set; } // 对象标识 public T ObjectKey { get; set; } // 超时秒数 public int TimeoutSeconds { get; set; } /// <summary> /// 以秒为单位的 Tick 值,由超时任务运行器根据当前时间加上超时秒数计算设置 /// DateTime.Ticks 是以 10ns(10纳秒) 为单位 /// 将其除以 10 单位为 ws(微秒),再除以 1000 为 ms(毫秒),再除以 1000 为 s(秒) /// 累计为 DateTime.Ticks / 10000000 /// </summary> public long ExecuteSecondTicks { get; set; } // 超时回调方法 public TimeoutCallback<T> Callback { get; set; } } 3.3超时任务清单

任务清单,在操作粒度上,可以以任务标识为单位,也可以以对象标识为单位,因此,为了快速检索。任务清单分两种形式存储,一种以任务标识为主键,另一种以对象标识为主键,其结构如下:

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

具体类型结构定义如下,_DictionaryLocker 有于同步加锁,确保线程安全。

// 以 TaskId(任务标识) 为 KEY 的任务清单字典 private Dictionary<long, TimeoutTask<T>> _TaskIdDictionary = new Dictionary<long, TimeoutTask<T>>(); // 以 ObjectId(任务相关对象标识) 为 KEY 的任务字典,因每个对象可以有多个超时任务,所以为列表 private Dictionary<T, List<TimeoutTask<T>>> _TaskObjectKeyDictionary = new Dictionary<T, List<TimeoutTask<T>>>(); // 用于同步操作上述两个清单字典,使得线程安全 private object _DictionaryLocker = new object(); 3.4任务执行队列

一个普通的先进先出的队列,_RunLocker 用于线程安全加锁。

// 已超时任务队列,由任务运行线程逐个执行 private Queue<TimeoutTask<T>> _TaskRunQueue = new Queue<TimeoutTask<T>>(); // 用来同步操作任务队列,使得线程安全(生产者,消费者模式) private object _RunLocker = new object(); 3.5超时检测者

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

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