sleep延迟函数替代方案及asyncio.Timer()运行方法咨询
一、sleep延迟函数的替代方案(适配百万级事件场景)
针对你需要支持百万用户、秒级定时且避免内存不足的需求,推荐以下几种替代方案:
协程异步调度(优先选择)
基于asyncio的异步调度是低内存消耗的核心方案,可替代传统time.sleep()或无管理的asyncio.sleep()。核心是避免为每个任务单独维护阻塞状态,通过事件循环统一调度。但直接给百万个任务创建asyncio.sleep()仍会有内存压力,需结合时间轮调度优化。时间轮调度
这是处理大规模定时任务的高效方案:将时间划分为固定间隔的"槽"(比如1秒/槽),把延迟相同或相近的任务归类到对应时间槽中,事件循环每秒处理当前槽的所有任务。这种方式无需为每个任务单独分配睡眠资源,内存开销极低,完全适配百万级任务场景。线程/进程池定时(不推荐百万级场景)
threading.Timer()或concurrent.futures.ThreadPoolExecutor适合少量定时任务,但每个线程会占用MB级内存,百万级任务会直接导致内存溢出,因此不适合你的游戏场景。
二、关于asyncio.Timer()的问题:不存在该API,正确实现方式
首先明确:asyncio模块中没有asyncio.Timer()这个官方API,你遇到的AttributeError正是因为这个原因。要实现asyncio环境下的定时任务,有两种高效方式:
方式1:基础版——asyncio.create_task() + asyncio.sleep()
适合中小规模任务,若要适配百万级需结合时间轮:
import asyncio async def handle_upgrade_complete(player_id, building_id): # 建筑升级完成的业务逻辑 print(f"玩家 {player_id} 的建筑 {building_id} 升级完成!") async def schedule_upgrade(delay_seconds, player_id, building_id): await asyncio.sleep(delay_seconds) await handle_upgrade_complete(player_id, building_id) async def main(): # 模拟创建百万级定时任务(示例,实际需结合任务管理优化) tasks = [] for player_idx in range(1000000): task = asyncio.create_task(schedule_upgrade(30, player_idx, "城堡")) tasks.append(task) await asyncio.gather(*tasks) asyncio.run(main())
方式2:优化版——自定义时间轮调度器(适配百万级任务)
针对百万级场景,时间轮能大幅降低内存占用:
import asyncio from collections import defaultdict class TimeWheelScheduler: def __init__(self, slot_interval=1): self.slot_interval = slot_interval # 每个时间槽的间隔(秒) self.task_slots = defaultdict(list) self.current_slot = 0 self.is_running = False async def start(self): self.is_running = True while self.is_running: # 处理当前时间槽的所有任务 if self.current_slot in self.task_slots: pending_tasks = self.task_slots.pop(self.current_slot) for task in pending_tasks: asyncio.create_task(task()) await asyncio.sleep(self.slot_interval) self.current_slot += 1 def add_timer_task(self, delay_seconds, task_func): # 计算任务所属的目标时间槽 target_slot = self.current_slot + delay_seconds // self.slot_interval self.task_slots[target_slot].append(task_func) # 使用示例 async def handle_upgrade(player_id, building_id): print(f"玩家 {player_id} 的建筑 {building_id} 升级完成!") async def main(): scheduler = TimeWheelScheduler() asyncio.create_task(scheduler.start()) # 批量添加百万个升级任务 for player_id in range(1000000): # 绑定参数时注意避免闭包陷阱 scheduler.add_timer_task(30, lambda pid=player_id: handle_upgrade(pid, "城堡")) # 运行60秒后停止调度器 await asyncio.sleep(60) scheduler.is_running = False asyncio.run(main())
额外提示
如果你的游戏需要持久化定时任务(比如服务器重启后不丢失未完成的升级任务),需将任务信息(玩家ID、建筑ID、剩余延迟时间)存储到数据库中,服务启动时从数据库读取并重新添加到调度器。
内容的提问来源于stack exchange,提问作者Abdelhamed Abdin

