本文目录导读:

我来详细介绍如何在Shell脚本中实现时间轮定时器。
基础时间轮实现
简单的时间轮定时器
#!/bin/bash
# 时间轮定时器实现
# 基本结构:时间槽数组 + 指针
# 时间轮配置
WHEEL_SIZE=60 # 时间槽数量(60秒轮转一圈)
TICK_INTERVAL=1 # 每次tick间隔(秒)
# 初始化时间轮
declare -a time_wheel
for ((i=0; i<WHEEL_SIZE; i++)); do
time_wheel[$i]=""
done
# 当前指针位置
current_position=0
# 添加定时任务
add_timer() {
local delay=$1 # 延迟时间(秒)
local command=$2 # 要执行的命令
# 计算目标槽位置
local target_slot=$(( (current_position + delay) % WHEEL_SIZE ))
# 将任务添加到对应槽
if [ -z "${time_wheel[$target_slot]}" ]; then
time_wheel[$target_slot]="$command"
else
time_wheel[$target_slot]="${time_wheel[$target_slot]}|$command"
fi
echo "Timer added: '$command' (slot $target_slot, delay ${delay}s)"
}
# 执行当前槽的任务
execute_slot() {
local slot=$1
local tasks=${time_wheel[$slot]}
if [ -n "$tasks" ]; then
IFS='|' read -ra task_array <<< "$tasks"
for task in "${task_array[@]}"; do
if [ -n "$task" ]; then
echo "[$(date '+%H:%M:%S')] Executing: $task"
eval "$task" &
fi
done
time_wheel[$slot]="" # 清空当前槽
fi
}
# 时间轮主循环
run_time_wheel() {
echo "Time wheel started (wheel size: $WHEEL_SIZE)"
while true; do
echo "[$(date '+%H:%M:%S')] Tick - Position: $current_position"
# 执行当前槽的任务
execute_slot $current_position
# 移动指针
current_position=$(( (current_position + 1) % WHEEL_SIZE ))
# 等待下一个tick
sleep $TICK_INTERVAL
done
}
# 使用示例
echo "=== 时间轮定时器示例 ==="
# 添加一些定时任务
add_timer 3 "echo 'Task 1: 3 seconds delay'"
add_timer 5 "echo 'Task 2: 5 seconds delay'"
add_timer 10 "echo 'Task 3: 10 seconds delay'"
add_timer 65 "echo 'Task 4: 65 seconds delay (wrap around)'"
# 启动时间轮(后台运行)
run_time_wheel &
wheel_pid=$!
# 运行一段时间后停止
sleep 15
kill $wheel_pid 2>/dev/null
echo "Time wheel stopped"
进阶版:多层时间轮
#!/bin/bash
# 多层时间轮实现
# 支持更长时间范围的定时器
# 配置参数
declare -A WHEEL_CONFIG
WHEEL_CONFIG[0,size]=60 # 第一层:60个槽,每个槽1秒
WHEEL_CONFIG[0,interval]=1
WHEEL_CONFIG[1,size]=60 # 第二层:60个槽,每个槽1分钟
WHEEL_CONFIG[1,interval]=60
WHEEL_CONFIG[2,size]=24 # 第三层:24个槽,每个槽1小时
WHEEL_CONFIG[2,interval]=3600
LAYERS=3
# 初始化所有层
declare -A time_wheel
init_wheels() {
for ((layer=0; layer<LAYERS; layer++)); do
local size=${WHEEL_CONFIG[$layer,size]}
for ((slot=0; slot<size; slot++)); do
time_wheel[$layer,$slot]=""
done
done
}
# 当前位置指针
declare -a current_positions
for ((i=0; i<LAYERS; i++)); do
current_positions[$i]=0
done
# 添加定时器到时间轮
add_timer_ml() {
local delay=$1
local command=$2
local layer=0
local remaining=$delay
# 找到合适的层级
for ((layer=0; layer<LAYERS; layer++)); do
local interval=${WHEEL_CONFIG[$layer,interval]}
if [ $remaining -lt $(( interval * ${WHEEL_CONFIG[$layer,size]} )) ]; then
break
fi
done
if [ $layer -ge $LAYERS ]; then
echo "Error: Delay too large"
return 1
fi
local interval=${WHEEL_CONFIG[$layer,interval]}
local size=${WHEEL_CONFIG[$layer,size]}
local pos=${current_positions[$layer]}
# 计算目标槽
local target_slot=$(( (pos + remaining / interval) % size ))
# 存储任务信息
time_wheel[$layer,$target_slot]="${time_wheel[$layer,$target_slot]}|$layer,$remaining,$command"
echo "Multi-layer timer added: '$command' (layer $layer, slot $target_slot)"
}
# 执行层级任务
execute_layer() {
local layer=$1
local pos=${current_positions[$layer]}
local size=${WHEEL_CONFIG[$layer,size]}
local interval=${WHEEL_CONFIG[$layer,interval]}
local tasks=${time_wheel[$layer,$pos]}
if [ -n "$tasks" ]; then
IFS='|' read -ra task_array <<< "$tasks"
for task_info in "${task_array[@]}"; do
if [ -n "$task_info" ]; then
IFS=',' read -r org_layer remaining command <<< "$task_info"
if [ $remaining -le $interval ]; then
# 任务到期,执行
echo "[$(date '+%H:%M:%S')] Executing: $command"
eval "$command" &
else
# 任务还有剩余时间,下移到更低层级
local new_delay=$((remaining - interval))
local new_layer=$((layer - 1))
if [ $new_layer -ge 0 ]; then
local new_interval=${WHEEL_CONFIG[$new_layer,interval]}
local new_pos=${current_positions[$new_layer]}
local new_slot=$(( (new_pos + new_delay / new_interval) % ${WHEEL_CONFIG[$new_layer,size]} ))
time_wheel[$new_layer,$new_slot]="${time_wheel[$new_layer,$new_slot]}|$new_layer,$new_delay,$command"
fi
fi
fi
done
time_wheel[$layer,$pos]=""
fi
}
# 多层时间轮主循环
run_multi_layer_wheel() {
init_wheels
while true; do
for ((layer=0; layer<LAYERS; layer++)); do
local interval=${WHEEL_CONFIG[$layer,interval]}
local size=${WHEEL_CONFIG[$layer,size]}
# 检查是否需要触发该层
if [ $(( $(date +%s) % interval )) -eq 0 ]; then
execute_layer $layer
current_positions[$layer]=$(( (current_positions[$layer] + 1) % size ))
fi
done
sleep 1
done
}
实用版本:带回调函数
#!/bin/bash
# 实用时间轮实现
# 定时器条目结构:ID|执行时间|命令|重复次数
declare -A timer_entries
timer_counter=0
# 时间轮配置
WHEEL_SIZE=100
TICK_MS=100 # 100ms tick
# 初始化
declare -a wheel
for ((i=0; i<WHEEL_SIZE; i++)); do
wheel[$i]=""
done
current_slot=0
# 注册定时器
register_timer() {
local delay_ms=$1
local command=$2
local repeat=${3:-1} # 默认执行1次,-1表示无限重复
local timer_id=$((timer_counter++))
local target_slot=$(( (current_slot + delay_ms / TICK_MS) % WHEEL_SIZE ))
local remaining_ticks=$(( delay_ms / TICK_MS ))
# 存储定时器信息
local entry="$timer_id|$remaining_ticks|$command|$repeat"
if [ -z "${wheel[$target_slot]}" ]; then
wheel[$target_slot]="$entry"
else
wheel[$target_slot]="${wheel[$target_slot]},$entry"
fi
echo "Timer #$timer_id registered: '$command' (${delay_ms}ms, $repeat times)"
return $timer_id
}
# 取消定时器
cancel_timer() {
local timer_id=$1
for ((i=0; i<WHEEL_SIZE; i++)); do
if [ -n "${wheel[$i]}" ]; then
IFS=',' read -ra entries <<< "${wheel[$i]}"
local filtered=""
for entry in "${entries[@]}"; do
local id=$(echo "$entry" | cut -d'|' -f1)
if [ "$id" != "$timer_id" ]; then
if [ -z "$filtered" ]; then
filtered="$entry"
else
filtered="$filtered,$entry"
fi
fi
done
wheel[$i]="$filtered"
fi
done
echo "Timer #$timer_id cancelled"
}
# 执行定时器
execute_timer() {
local entry=$1
IFS='|' read -r timer_id remaining_ticks command repeat <<< "$entry"
if [ $remaining_ticks -le 0 ]; then
# 执行命令
echo "[$(date '+%H:%M:%S.%N')] Executing #$timer_id: $command"
eval "$command" &
# 检查是否需要重复
if [ $repeat -gt 1 ] || [ $repeat -eq -1 ]; then
# 重新注册
if [ $repeat -gt 1 ]; then
register_timer $(($TICK_MS * ${WHEEL_SIZE:-100})) "$command" $((repeat - 1))
else
register_timer $(($TICK_MS * ${WHEEL_SIZE:-100})) "$command" -1
fi
fi
else
# 剩余时间减少,移到合适的位置
local new_remaining=$((remaining_ticks - 1))
local new_slot=$(( (current_slot + new_remaining) % WHEEL_SIZE ))
local new_entry="$timer_id|$new_remaining|$command|$repeat"
if [ -z "${wheel[$new_slot]}" ]; then
wheel[$new_slot]="$new_entry"
else
wheel[$new_slot]="${wheel[$new_slot]},$new_entry"
fi
fi
}
# 时间轮主循环
start_wheel() {
echo "Time wheel started (${TICK_MS}ms per tick)"
while true; do
# 处理当前槽
local entries=${wheel[$current_slot]}
if [ -n "$entries" ]; then
IFS=',' read -ra entry_list <<< "$entries"
for entry in "${entry_list[@]}"; do
if [ -n "$entry" ]; then
execute_timer "$entry"
fi
done
fi
# 清空当前槽
wheel[$current_slot]=""
# 移动指针
current_slot=$(( (current_slot + 1) % WHEEL_SIZE ))
# 等待
sleep $(echo "scale=3; $TICK_MS/1000" | bc)
done
}
# 使用示例
echo "=== 实用时间轮示例 ==="
# 注册定时器
register_timer 500 "echo 'Fast task (500ms)'"
register_timer 1000 "echo '1 second task'"
register_timer 3000 "echo '3 seconds task'" 2
register_timer 5000 "echo '5 seconds task (repeat)'" -1
# 启动时间轮
start_wheel &
wheel_pid=$!
# 运行一段时间后取消一个定时器
sleep 2
cancel_timer 3
sleep 8
kill $wheel_pid 2>/dev/null
echo "Time wheel stopped"
特性对比
| 特性 | 简单时间轮 | 多层时间轮 | 实用时间轮 |
|---|---|---|---|
| 时间精度 | 1秒 | 可配置 | 毫秒级 |
| 最大延迟 | 60秒 | 24小时+ | 可配置 |
| 任务重复 | 否 | 否 | 是 |
| 任务取消 | 否 | 否 | 是 |
| 复杂度 | 低 | 中 | 高 |
使用建议
- 选择合适的粒度:根据需求调整tick间隔
- 处理任务超时:添加超时检测机制
- 避免累积延迟:使用
date命令获取准确时间 - 资源管理:及时清理已完成的任务
这些实现可以根据你的具体需求进行调整和扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播