/// <summary> /// 添加任务 /// </summary> /// <param name="task">延时任务</param> /// <returns></returns> public bool AddTask(TimeTask task) { if (!task.IsWaiting) { return(false); } if (task.TimeoutMs < _currentNeedle + _tickSpan) { // 任务已经过期,无法添加 return(false); } // 是否可以放入当前时间轮 if (task.TimeoutMs < _currentNeedle + _wheelSpan) { // 任务已经过期,无法添加 // 计算过期时间戳所属的时间槽 var tickCount = task.TimeoutMs / _tickSpan; var slotIndex = (int)(tickCount % _slotCount); var slot = _timeSlots[slotIndex]; slot.AddTask(task); // 设置成功,说明该时间槽已过期出队,需要重新入队 // 在同一轮循环内,同一个槽的slotTimeoutMs是一样的 var slotTimeoutMs = tickCount * _tickSpan; if (slot.SetExpiration(slotTimeoutMs)) { // 注意这里有个特殊情况: // slotTimeoutMs是按照tickSpan裁剪得到的值,可能会小于当前时间, // 意味着这里入队的slot已经超时,TimingWheelTimer会将该slot立即出队。 _delayQueue.TryAdd(slot); /* * 举个例子,需要结合TimingWheelTimer.Step方法来分析: * 假如第1层时间轮是秒级(1s 60个槽),那么第2层时间轮就是分钟级(60s 60个槽),第3层时间轮是小时级(3600s,60个槽); * 第1层时间轮启动时间是12点钟(currentNeedle=12:00:00),1小时1分后(当前时间13:01:00)加入第1个延时任务,延时时间是1s; * 该任务TimeoutMs是13:01:01,虽然是1s后过期,但由于currentNeedle=12:00:00,所以计算后实际会进入第3层时间轮; * 在第3层时间轮计算得到的slotTimeoutMs为13:00:00,已过期,所以solt在入队后又会立即出队(由TimingWheelTimer.Step.TryTake处理); * 那么出队后重新计算,第1层时间轮的currentNeedle会变成13:00:00,所以计算后任务会进入第2层时间轮; * 在第2层时间轮计算得到的slotTimeoutMs为13:01:00,还是过期,所以solt在入队后又会立即出队(由TimingWheelTimer.Step.TryTakeNoBlocking处理); * 那么出队后重新计算,第1层时间轮的currentNeedle会变成13:01:00,延时任务将留在第1层时间轮,等待1s后过期。 */ } return(true); } // 超出当前时间轮,则放入下一层 else { CreateNextWheel(); return(_nextWheel.AddTask(task)); } }
/// <summary> /// 添加任务 /// </summary> /// <param name="timeTask">延时任务</param> private void AddTask(TimeTask timeTask) { // 添加失败,说明该任务已到期,需要执行了 if (!_timingWheel.AddTask(timeTask)) { if (timeTask.IsWaiting) { // TODO:是否放入自定义线程池 Task.Run(timeTask.Run); } } }
/// <summary> /// 添加任务 /// </summary> /// <param name="timeoutMs">过期时间戳,绝对时间</param> /// <param name="delegateTask">延时任务</param> /// <returns></returns> public ITimeTask AddTask(long timeoutMs, Action delegateTask) { Requires.NotNull(delegateTask, nameof(delegateTask)); _lock.EnterReadLock(); try { var task = new TimeTask(timeoutMs, delegateTask); AddTask(task); return(task); } finally { _lock.ExitReadLock(); } }
/// <summary> /// 移除定时任务 /// </summary> /// <param name="task"></param> /// <returns></returns> public bool RemoveTask(TimeTask task) { lock (_lock) { if (task.TimeSlot == this) { if (_tasks.Remove(task)) { task.TimeSlot = null; _taskCount.Decrement(); return(true); } return(false); } } return(false); }
/// <summary> /// 添加任务 /// </summary> /// <param name="task">延时任务</param> /// <returns></returns> public bool AddTask(TimeTask task) { if (!task.IsWaiting) { return(false); } if (task.TimeoutMs < _currentNeedle + _tickSpan) { // 任务已经过期,无法添加 return(false); } // 是否可以放入当前时间轮 if (task.TimeoutMs < _currentNeedle + _wheelSpan) { // 任务已经过期,无法添加 // 计算过期时间戳所属的时间槽 var tickCount = task.TimeoutMs / _tickSpan; var slotIndex = (int)(tickCount % _slotCount); var slot = _timeSlots[slotIndex]; slot.AddTask(task); // 设置成功,说明该时间槽已过期出队,需要重新入队 // 在同一轮循环内,同一个槽的slotTimeoutMs是一样的 var slotTimeoutMs = tickCount * _tickSpan; if (slot.SetExpiration(slotTimeoutMs)) { _delayQueue.TryAdd(slot); } return(true); } // 超出当前时间轮,则放入下一层 else { CreateNextWheel(); return(_nextWheel.AddTask(task)); } }
/// <summary> /// 添加定时任务 /// </summary> /// <param name="task"></param> /// <returns></returns> public void AddTask(TimeTask task) { var done = false; while (!done) { // 先从其它队列移除掉 // 在lock之外操作,避免死锁 task.Remove(); lock (_lock) { if (task.TimeSlot == null) { _tasks.AddLast(task); task.TimeSlot = this; _taskCount.Increment(); done = true; } } } }