参考云风大神的例子,对其进行了改进,支持屡次提交单个日程,改变时间后,提早日程触发时间。api
--[[ t提供了两种方案 方案1和2 ]] local skynet = require "skynet" local service = require "skynet.service" local schedule = {} local service_addr -- ttime = { month=, day=, wday=, hour= , min= } function schedule.submit(ttime) return skynet.call(service_addr, "lua", ttime) -- TOO(do somthing) -- skynet.error(" schedule.submit") end function schedule.changetime(ttime) local tmp = {} for k, v in pairs(ttime) do tmp[k] = v end tmp.changetime = true return skynet.call(service_addr, "lua", tmp) end skynet.init(function() local schedule_service = function() local skynet = require "skynet" local task = {session = 0,difftime = 0} local tasksession = {} local function next_time(now, ttime) -- 参数如今的时间,日程时间 local ntime = { year = now.year, month = now.month, day = now.day, hour = ttime.hour or 0, min = ttime.min or 0, sec = ttime.sec, } -- skynet.error(now.year,now.month,now.day,now.hour,now.min,now.sec,now.wday) -- skynet.error(ttime.year,ttime.month,ttime.day,ttime.hour,ttime.min,ttime.sec,ttime.wday) -- skynet.error(ntime.year,ntime.month,ntime.day,ntime.hour,ntime.min,ntime.sec,ntime.wday) if ttime.wday then -- 每周的活动 -- set week assert(ttime.day == nil and ttime.month == nil) ntime.day = ntime.day + ttime.wday - now.wday local t = os.time(ntime) if t < now.time then ntime.day = ntime.day + 7 end else -- 若是日程时间使日期的话 -- set day,no week day if ttime.day then ntime.day = ttime.day end if ttime.month then ntime.month = ttime.month end local t = os.time(ntime) -- 换成秒 if t < now.time then --时间和如今比是过去的时间变成下个月或者下一年 if ttime.month then ntime.year = ntime.year + 1 else ntime.month = ntime.month + 1 end end end skynet.error("next_time ",ntime.year,ntime.month,ntime.day,ntime.hour,ntime.min,ntime.sec,ntime.wday) return os.time(ntime) -- 将下第二天程的时间换成秒返回 end local function changetime(ttime) local ctime = math.floor(skynet.time()) --向下取整 --local now = math.floor(skynet.time()) skynet.error("changetime",ctime) local current = os.date("*t",ctime) --将ctime 转换成*t的表结构 current.time = ctime -- 保存原来的时间 if not ttime.hour then -- 若是原来表结构中没有小时,将如今的小时赋值给它 ttime.hour = current.hour end if not ttime.min then -- 若是原来表结构中没有分钟,将如今的分钟赋值给它 ttime.min = current.min end ttime.sec = current.sec -- 如今的秒赋值给它 local ntime = next_time(current,ttime) -- 计算出下一第二天程的时间 skynet.error(string.format("Change time to nex %s",os.date(nil,ntime))) skynet.error(string.format("Change time to cur %s",os.date(nil,ctime))) local unique_diff = os.difftime(ntime,ctime) -- 方案1计算出下第二天程距离如今时间还有多少秒 --task.difftime = os.difftime(ntime,ctime) --skynet.error("changetime task.difftime",task.difftime) skynet.error("changetime unique_diff",unique_diff) for k, v in pairs(task) do -- 日期改变了才 遍历日程表,唤醒全部挂起的线程 local ttask_diff = 0 --方案1 if type(v) == "table" then ttask_diff = os.difftime(v.time, math.floor(skynet.time())) -- 方案1 if unique_diff > ttask_diff then --方案1 task[k].difftime = ttask_diff --方案1 --task.difftime = ttask_diff end skynet.error("changetime unique_diff",k,unique_diff) skynet.error("changetime task.difftime",k,task.difftime) skynet.error("changetime ttask_diff",k,ttask_diff) skynet.error("changetime session",k, type(v.co)) skynet.wakeup(v.co) -- 唤醒服务 end end skynet.ret() end -- {year = 2020, month=12, day=31, hour=20 , min=30} local function submit(_, addr, ttime) if ttime.changetime then return changetime(ttime) end local session = task.session + 1 task.session = session task[session] = {time = 0, address = addr, difftime = 0}-- 方案1 skynet.error("submit task.session",task.session) repeat -- 循环 直到时间到了 local now = math.floor(skynet.time()) -- 真的当前时间戳 skynet.error(string.format("submit now %s",os.date(nil,now))) local ctime = now + task[session].difftime -- 方案1如今的时间+日程距离如今的秒数 = 假的当前的时间戳 local ctime = now + task.difftime -- 方案2 skynet.error("submit task[session].difftime",session,task[session].difftime) -- if ctime > task[session].time then -- 方案2 -- break -- 方案2 -- end-- 方案2 local current = os.date("*t",ctime) -- 变成结构 current.time = ctime -- 增长一项time local ntime = next_time(current,ttime) -- 计算下第二天程时间 skynet.error(string.format("submit ntime %s",os.date(nil,ntime))) skynet.error(string.format("submit ntime %d",ntime)) skynet.error(string.format("submit ctime %s",os.date(nil,ctime))) task[session].time = ntime -- 方案1 task[session].co = coroutine.running()-- 方案1 local diff = os.difftime(ntime,ctime) skynet.error("submit sleep",diff,session) until skynet.sleep(diff * 100) ~= "BREAK" -- 休眠diff * 100 task[session] = nil skynet.ret() end skynet.start(function() skynet.dispatch("lua", submit) skynet.info_func(function() local info = {} for k, v in pairs(task) do if type(v) == "table" then table.insert(info, { time = os.date(nil, v.time), address = skynet.address(v.address), }) end return info end end) end) end service_addr = service.new("schedule", schedule_service) -- 启动一个服务 end) return schedule
方案一主要思路是:在repeat循环中每次都会计算一个假的当前时间。经过假的当前时间计算出日程的下次触发时间,而后计算下第二天程的触发时间和假的当前时间的差值来决定线程挂起的时长。如何改变假的当前时间是经过changetime接口改变的。在这个api中会改变task.difftime的值,而后把所有线程全都唤醒,继续执行repeat循环,从新计算下第二天程的触发时间,而后从新计算假的当时间和下次触发的时差=线程挂起的时长。若是改变的时刻在日程触发时刻的后面,要求改变时刻以前的日程所有触发,方案1和2均可以,若是提交了乐意日程触发时刻比如今真实的会见还靠前,则方案2不可行(不过这种状况通常不会有)session