Skip to content

进阶实战 07 · 定时任务 cron:定时提醒器 ​

《LingBuilder 进阶实战系列》第 7 集。「每天早上 9 点提醒我交日报」「每小时备份一次」——这类按日历时间干活的需求,靠秒表式的循环数不靠谱,正解是 cron 表达式。本集用定时任务模块(lingbuilder.cron)做一个定时提醒器:填一句表达式,到点自动写日志、弹窗提醒;还能暂停、恢复、立即试触发。你还会搞清「时钟组件」和「cron」到底各管什么。全程约 30 分钟。

前置要求:学完零基础入门系列与进阶前 6 集(特别是第 2 集的时钟组件和第 6 集的「失败就问取错误」)。本集源码包见文末。

这一集你将学会 ​

  • cron_定时_启动(表达式, &处理器):按 cron 表达式注册周期任务
  • cron 表达式入门:五段「分 时 日 月 周」,*、*/n、a-b、逗号列表
  • 三件辅助命令:cron_定时_校验表达式、cron_定时_取表达式错误、cron_定时_表达式说明(把表达式翻译成人话)
  • 生命周期管理:cron_定时_暂停 / cron_定时_恢复 / cron_定时_停止 / cron_定时_下次触发时间
  • 演示利器 cron_定时_立即触发:不用干等整分,立刻触发一次
  • 状态守卫:用 cron_定时_取状态 判断「等待中 / 已暂停 / 已停止 / 不存在」

1. 三十秒理解:时钟管间隔,cron 管日历 ​

第 2 集用过时钟组件:每隔固定毫秒触发一次,适合「每秒刷新界面」这种间隔型任务。但它回答不了「每天 9 点」——一天 86400 秒,从哪个时刻数起?重启了怎么办?跨过午夜怎么办?

cron 表达式按日历时间匹配:五段分别是「分 时 日 月 周」,每一到点命中就触发,不依赖程序什么时候启动:

表达式含义
*/1 * * * *每 1 分钟
0 9 * * *每天 09:00
30 8 * * 1-5工作日(周一到周五)08:30
0 */2 * * *每 2 小时(整点)
0 0 1 * *每月 1 号零点

记不住写法也没关系——本集会教模块自带的人话翻译,写完先「检查表达式」看看它理解得对不对。

到点后处理器在注册窗口的 UI 线程执行,可以直接更新控件;窗口关闭时任务自动停止,不会留下后台僵尸。

2. 新建项目与启用模块 ​

新建窗口项目:名称 定时提醒器,模板「Windows 界面设计」,创建位置留空:

新建项目

启用「定时任务模块」(29 条命令,本集用其中 10 条)。

3. 设计界面 ​

控件名称显示内容左距顶距宽度高度
标签说明标签用 cron 表达式约定时间,到点自动弹窗提醒302256024
标签表达式标签cron 表达式(分 时 日 月 周)305632022
编辑框表达式框(空)308256028
标签内容标签提醒内容3012032022
编辑框提醒内容框(空,勾选多行)3014656064
按钮校验按钮检查表达式3022217632
按钮启动按钮启动定时提醒22222217632
按钮触发按钮立刻试一次41422217632
按钮暂停按钮暂停3026217632
按钮恢复按钮恢复22226217632
按钮停止按钮停止提醒41426217632
列表框结果列表—30306560120
标签状态标签就绪3043456022

窗口宽 620、高 500。绑 6 个按钮事件 + 窗口「创建完毕」。

设计器全景

4. 写代码 ①:创建完毕与启动 ​

创建完毕预填一个每分钟的表达式和提醒内容;启动按钮先校验、再注册:

lcpp
事件 _MainWindow_创建完毕()
    控件_设置文本(表达式框, "*/1 * * * *")
    控件_设置文本(提醒内容框, "该站起来活动一下啦!")
    控件_设置文本(状态标签, "就绪。先「检查表达式」,再「启动定时提醒」")
结束

事件 _启动按钮_被单击()
    局部 文本型 表达式
    表达式 = 控件_取文本(表达式框)
    如果 (cron_定时_校验表达式(表达式) = 假)
        控件_设置文本(状态标签, "表达式非法,先点「检查表达式」看原因")
        返回()
    如果结束
    当前任务 = cron_定时_启动(表达式, &到点提醒)
    控件_清空项目(结果列表)
    如果 (当前任务 > 0)
        控件_添加项目(结果列表, "已启动:任务编号 " + 到文本(当前任务))
        控件_添加项目(结果列表, cron_定时_表达式说明(表达式) + ",下次触发:" + cron_定时_下次触发时间(当前任务))
        控件_设置文本(状态标签, "定时提醒运行中,到点会写进列表并弹窗")
    否则
        控件_添加项目(结果列表, "启动失败:" + cron_定时_取错误())
        控件_设置文本(状态标签, "启动失败,原因见结果列表")
    如果结束
结束

启动代码

拆解:

  • cron_定时_启动(表达式, &到点提醒) 返回任务编号(定时任务 类型,64 位、进程内不复用);编号大于 0 = 启动成功。处理器引用照旧写 &到点提醒。
  • cron_定时_表达式说明 把表达式翻成人话(「每分钟」),cron_定时_下次触发时间 给出精确到秒的下次触发时刻——两行结果一贴,用户立刻知道任务什么时候干活。
  • 当前任务、到点次数 是两个项目全局变量(全局 定时任务 当前任务、全局 整数型 到点次数)——任务编号要跨按钮保存。

本集真实编译错误:最初版本我在停止按钮里写了 当前任务 = 0 想清空编号,编译直接被拦——「不能把 整数型 赋值给 定时任务 变量 当前任务」。定时任务 是模块专属类型,不接受整数赋值,也别用 0 当哨兵:

类型错误

正确姿势是用 cron_定时_取状态(任务) 查状态(返回「等待中 / 已暂停 / 已停止 / 不存在」),本集所有按钮的守卫都改成按状态判断。

5. 写代码 ②:到点提醒与状态守卫 ​

到点处理器:签名必须是 空 处理器(定时任务 任务):

lcpp
空 到点提醒(定时任务 任务)
    到点次数 = 到点次数 + 1
    控件_添加项目(结果列表, "【第 " + 到文本(到点次数) + " 次到点】" + 控件_取文本(提醒内容框))
    控件_设置文本(状态标签, "到点啦!第 " + 到文本(到点次数) + " 次提醒已触发")
    调试输出("到点提醒:任务 " + 到文本(任务))
    信息框(控件_取文本(提醒内容框), 64, "定时提醒")
结束

到点时先把记录写进列表、再弹一个信息框(64 = 信息图标)。手动点「检查表达式」和「立刻试一次」负责触发与状态切换:

lcpp
事件 _触发按钮_被单击()
    局部 文本型 状态
    状态 = cron_定时_取状态(当前任务)
    如果 (状态 <> "等待中")
        控件_设置文本(状态标签, "任务当前是「" + 状态 + "」,等运行中才能试")
        返回()
    如果结束
    cron_定时_立即触发(当前任务)
结束

事件 _暂停按钮_被单击()
    局部 文本型 状态
    状态 = cron_定时_取状态(当前任务)
    如果 (状态 <> "等待中")
        控件_设置文本(状态标签, "任务当前是「" + 状态 + "」,等运行中才能暂停")
        返回()
    如果结束
    如果 (cron_定时_暂停(当前任务))
        控件_添加项目(结果列表, "已暂停:到点不触发也不补发")
        控件_设置文本(状态标签, "已暂停,点「恢复」继续")
    否则
        控件_添加项目(结果列表, "暂停失败:" + cron_定时_取错误())
    如果结束
结束

cron_定时_立即触发 是演示神器:不用等下一个整分,立刻按注册方式触发一次,且不影响原有周期。恢复、停止两个按钮同一套路:先 cron_定时_取状态 守卫,再调 cron_定时_恢复 / cron_定时_停止,成功失败都写进列表。

6. 编译与运行 ​

点「编译当前项目」,错误列表 (0)、输出窗口「编译成功」「生成完成」:

编译成功

6.1 先故意写错:非法表达式 ​

把表达式改成一句中文「每晚八点」,点「检查表达式」:

非法表达式

模块不跟你客气:「cron 表达式必须为 5 段(分 时 日 月 周)或 6 段(秒 分 时 日 月 周),当前是 1 段」——中文原因直接说清。改回 */1 * * * * 再检查:

表达式合法

翻译成人话:每分钟。

6.2 启动并立刻试一次 ​

点「启动定时提醒」:

启动成功

结果列表报出任务编号、「每分钟,下次触发:2026-10-19 02:43:00」——下次触发的时刻精确到秒。到点了(或点「立刻试一次」)会发生什么:

到点触发

结果列表写入「【第 1 次到点】该站起来活动一下啦!」,状态栏同步报「到点啦」,同时弹出「定时提醒」信息框(截图采集时信息框被置顶的采集窗口挡住,你自己运行会正常看到弹窗)。

6.3 暂停与停止 ​

点「暂停」:

暂停

「已暂停:到点不触发也不补发」——暂停期间的到点不会被补发,恢复后从当前时间重新计算下次触发。点「停止提醒」:

停止

任务彻底停止;想再来一轮,换个表达式重新启动即可。

7. cron 还能做什么(进阶速览) ​

本集用了 10 条命令,模块一共 29 条,还有三块进阶能力先混个脸熟:

  • crontab 表文件:cron_定时_表添加 / cron_定时_表保存 / cron_定时_表载入运行 把任务持久化成表文件(默认在 %APPDATA%\LingBuilder\cron\crontab.txt),下次启动一键载入。
  • 后台线程型任务:cron_定时_提交线程(表达式, &工作处理器, &完成处理器) 到点先在后台线程干活、干完回 UI 线程汇报——和第 4 集的线程纪律一样:工作处理器禁碰控件。
  • 开机自启 + MAILTO:cron_定时_开机自启(真) 把本程序注册为开机自启的 cron 守护;命令型任务失败时还能经 cron_定时_邮件配置 发通知邮件(第 8 集的邮件模块正好接上)。

8. 常见问题急救表 ​

症状原因解法
编译报「不能把 整数型 赋值给 定时任务 变量」给 定时任务 类型赋了 0 或其他整数用 cron_定时_取状态 判状态,别用整数哨兵
「表达式非法:…当前是 1 段」cron 必须五段/六段,空格分开分 时 日 月 周 五段,如 0 9 * * *
到点没反应表达式写得太远(如 0 9 * * * 要等到早上 9 点)先 cron_定时_立即触发 验证,再等真实到点
暂停期间错过的到点没补cron 暂停语义就是「不补发」需要必达的任务改用表文件 + 守护模式
处理器没触发&处理器名 与子程序名不一致名字必须一字不差(第 3 集老坑)
想要「每天 9 点」却写成 9 * * * *第二段才是小时,第一段是分钟0 9 * * *;写完用 表达式说明 校对

本集小结 ​

  • 时钟管间隔,cron 管日历时间点;「每天/每小时/工作日」类需求用 cron。
  • 一行启动:当前任务 = cron_定时_启动(表达式, &到点提醒);任务编号是 定时任务 类型,别拿整数赋值。
  • 校验三件套:校验表达式 → 取表达式错误(中文原因)→ 表达式说明(人话翻译)。
  • 状态守卫用 cron_定时_取状态;演示用 cron_定时_立即触发,不用干等。
  • 到点处理器在 UI 线程执行,可直接碰控件;窗口关闭任务自动停。

下一集预告:《邮件发送 mail》——给你的程序装上「发信能力」,学会 SMTP 配置与授权码,做一个一键发通知邮件的小工具,顺便把上一集 cron 的 MAILTO 通知补全。

回系列总览