用sleep()定时刷新缓存的关键是精准唤醒、防堆积、不丢任务,适用于低频轻量场景;需明确缓存结构与刷新逻辑、采用目标时间计算休眠时长、规避异常中断与系统时间跳变等常见问题。

用 sleep() 做定时刷新本地缓存,关键不是“睡多久”,而是“怎么醒得准、不堆积、不丢任务”。它适合低频、轻量、无需高精度的场景(比如每 30 秒查一次配置文件、每分钟同步一次本地计数器),不适合替代消息队列或分布式调度。
一、明确缓存结构和刷新逻辑
别急着写循环,先想清楚三件事:
-
缓存存什么:建议用线程安全的结构,如 Python 的
threading.local()(单线程隔离)、concurrent.futures.ThreadPoolExecutor配合dict加锁,或直接用functools.lru_cache+ 手动清除(适合只读+定期重载); - 刷新从哪来:是读本地 JSON/YAML 文件?查 SQLite?还是调一个本地 HTTP 接口?确保这个操作是幂等、快速、失败可容忍的(加 try/except + 日志);
- 刷新是否需要原子切换:避免读到“半更新”状态。推荐“新数据加载完成 → 原子替换引用”模式,例如:
二、写一个稳当的看门狗主循环
核心原则:每次 sleep 前计算下次执行时间,而不是固定 sleep 固定秒数 —— 防止因处理延迟导致任务越积越多。
- 用
time.time()记录上一次执行时间,下一次目标时间 = 上次时间 + 间隔; - 每次循环开头算
sleep_time = max(0, next_run_at - time.time()),再 sleep; - 捕获
KeyboardInterrupt或自定义退出信号(如收到SIGTERM),做清理后退出; - 加简单日志(例如用
print(f"[{datetime.now()}] Refreshed cache")或logging.info),方便排查卡死或频率异常。
三、避免常见坑
- 别在 sleep 里做耗时操作:把加载、解析、校验全放在 sleep 之后、更新之前;
- 别让异常中断整个循环:刷新逻辑必须包在 try/except 里,记录错误但继续下一轮;
-
别忽略时区和系统时间跳变:如果机器可能 NTP 校时,用
time.monotonic()更稳妥(它不受系统时间调整影响,适合测间隔); -
多实例要防重复刷新:如果是多个进程共用同一份缓存文件,需加文件锁(如
flock或portalocker),否则可能写冲突。
四、给个最小可运行示例(Python)
以下代码每 15 秒读一次 config.json,更新内存缓存,并支持 Ctrl+C 安全退出:
cache = {}
running = True
def load_config():
try:
with open("config.json") as f:
return json.load(f)
except Exception as e:
print(f"Load failed: {e}")
return {}
def signal_handler(sig, frame):
global running
print("Shutting down...")
running = False
signal.signal(signal.SIGINT, signal_handler)
last_run = time.monotonic()
interval = 15.0
while running:
now = time.monotonic()
if now >= last_run + interval:
cache = load_config()
print(f"Cache updated at {time.strftime('%H:%M:%S')}")
last_run = now
time.sleep(min(1.0, last_run + interval - now)) # 避免忙等,但每秒检查一次










