Beispiel #1
0
        /// <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));
            }
        }
Beispiel #2
0
        /// <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));
            }
        }