Liwendong.Frameworks.Tasks 2.0.1

dotnet add package Liwendong.Frameworks.Tasks --version 2.0.1
                    
NuGet\Install-Package Liwendong.Frameworks.Tasks -Version 2.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Liwendong.Frameworks.Tasks" Version="2.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Liwendong.Frameworks.Tasks" Version="2.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Liwendong.Frameworks.Tasks" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Liwendong.Frameworks.Tasks --version 2.0.1
                    
#r "nuget: Liwendong.Frameworks.Tasks, 2.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Liwendong.Frameworks.Tasks@2.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Liwendong.Frameworks.Tasks&version=2.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Liwendong.Frameworks.Tasks&version=2.0.1
                    
Install as a Cake Tool

Liwendong.Frameworks.Tasks

自定义任务

版本历史

2.0.1 (2026-10-17)

  • 修复:NewTaskExt 调度循环改用 CancellationTokenSource 关联 IsStop/移除,Task.Delay 带取消令牌;StopAllTask/RemoveTaskControlItem 时 Cancel,不再残留空转线程
  • 修复:MemoryTasksManage.ReleaseRunningIndex 的"读-比-删"加进程内锁串行化(跨进程原子性限制见说明);_taskCacheLazy 加 volatile 保证置换后发布可见性
  • 优化:State/Tips/LastExecutionTime/LastExecutionCompleteTime 补 XML 注释注明"外部勿写"(保守保留 public set,避免破坏公共 API)

更新日志

V2.0.0 (2026-10-15)

本版包含多项公开 API 破坏性变更(拼写修正与集合只读化),升级需重新编译并按下方清单替换

破坏性变更(按清单全局替换即可):

  1. TaskRuningTypes 枚举更名为 TaskRunningTypes(修正拼写);受影响成员:TaskControlItem.TaskRuningType 属性、TaskControlItem.GetTaskRuningTypes 方法、TaskViewModel 的类型描述来源等
  2. TaskRuningTypes.FiexTime(固定时间)已删除:该枚举值从未被配置识别产生(GetTaskRuningTypes 不会返回它),属死代码;短时间配置 10:00:00 自 V1.5 起由周期性(*-*-* 10:00:00)覆盖,语义不变
  3. ITask.DefaultSart 更名为 DefaultStart
  4. TaskControlItem.TimeSpanSencond 更名为 TimeSpanSecond
  5. TaskControlItem.LastExecutionComplateTime、TaskViewModel.LastExecutionComplateTime 更名为 LastExecutionCompleteTime
  6. TaskViewModel.RuningTypeDescription 更名为 RunningTypeDescription
  7. TaskHelper.TaskControlItems 由 List<TaskControlManage> 改为 IReadOnlyList<TaskControlManage>(每次访问返回集合快照):直接 Add/Remove/Clear 的用法改用 AddTaskControlItem / RemoveTaskControlItem;枚举遍历不再受其他线程增删影响
  8. MemoryTaskItem.State 由 int 改为 MemoryTaskState 枚举(值不变:Running=0、Completed=1、Error=-1,存量缓存数据兼容):item.State == 0 改为 item.State == MemoryTaskState.Running
  9. MemoryTasksManage 缓存 key 布局由 {前缀}{任务ID} 调整为 {前缀}task:{任务ID},并新增运行中索引 {前缀}running:{任务关键字};升级后旧布局的在途任务(存活期不超过缓存秒数,默认 1 小时)不再可见,会自然过期

修复与优化:

  • 修复(P1):MemoryTasksManage 判存由"全库扫描"改为"运行中索引"O(1) 判断。原实现在 Redis 缓存下每次操作都执行 KEYS 全库扫描(O(N) 且阻塞 Redis 单线程),且 CreateMemoryTasks 将扫描置于跨进程互斥锁内,锁持有时间被放大到整个扫描时长;现在创建/完成/出错/判存均不再触发扫描
  • 修复(P1):TaskHelper.TaskControlItems 公开可变集合、外部无法安全访问的问题(见破坏性变更 7)
  • 修复:GetAboutTasks 双重检查锁定未用 volatile 的发布可见性问题,改为可置换的 Lazy<List<ITask>>(RefreshTaskCache 语义不变)
  • 修复:Only 型(只执行一次)任务执行后修改 Setting 永不恢复的问题:Only 型配置变更时重置执行记录,改成未来的新时间后可重新触发;间隔/周期型任务改配置不重置(仍从上次完成时间起算)
  • 修复:周期性调度年份回滚在年份 9999 时 AddYears 抛异常的问题(其他字段早有上限保护,年份补齐;达上限时结果为"无下次执行时间"而非异常)
  • 修复:ErrorMemoryTasks 在 TasksRemark 为空时产出前导分号(";出错原因:xxx")的问题
  • 修复:CreateMemoryTasks 原地修改调用方传入的 info、同一实例复用两次会被覆盖的问题:内部改用克隆副本
  • 优化:内存任务缓存 key 前缀支持经配置项 MemoryTasksCacheKeyPrefix 覆盖(默认值不变),多个应用共享同一 Redis 时可避免互相串扰
  • 优化:MemoryTaskItem.State 魔法数字(0/1/-1)改为 MemoryTaskState 枚举;清理 csproj 中无作用的空 DocumentationFile 配置,开启 XML 文档输出
  • 优化:Setting 变更时同步重置"未执行通知"的去重记录,新的状态/提示能再次通知到调用方

已知限制:

  • 多节点部署:MemoryTasksManage 的"判存 + 插入"与 CompletedTotal += 1 读-改-写依赖命名互斥锁(只在本机会话域有效)。Redis + 多机部署时,两个节点可能同时通过"已存在正在执行的任务"检查,或并发自增丢失更新;单机/单进程部署无此问题。彻底解决需在缓存层提供 Redis 原子操作(SETNX/Lua),后续版本提供
  • GetMemoryTasks() 在 Redis 缓存下为前缀扫描(KEYS 命令,O(N)),请仅用于管理/展示路径,勿在高频路径调用
  • 依赖的 Liwendong.Utility 2.8.10 的 GetAppSettings(name, defaultValue) 在配置项缺失时返回 default(T) 而非默认值(后续版本已修复):MemoryTasksManage 已对缓存秒数(MemoryTasksCacheSecond)与 key 前缀(MemoryTasksCacheKeyPrefix)做兜底,行为不受影响;发包时建议将 Liwendong.Frameworks 包引用升级到含该修复的版本

V1.7.2 (2026-10-15)

  • 修复(重要):FullTimeRegex 正则少一个右括号导致模式无法编译,一旦经过该正则(任何非纯数字的时间配置)即使 TaskControlItem 类型初始化崩溃(RegexParseException → TypeInitializationException)的问题。该问题自正则加固起一直存在:短时间 10:00:00、完整时间 2010-01-01 10:00:00、周期性等配置走到类型识别即整体抛异常,且因 beforefieldinit 时序不确定,触发点不稳定;纯数字间隔配置因短路判断才幸免
  • 修复:CustomTask 批量注册时,单个任务的时间配置非法会抛异常导致整批任务注册失败的问题;现在只跳过该任务并记录日志,不影响其他任务(启动期单点配置错误不再扩散)
  • 修复:Setting 赋值只校验时间间隔型(纯数字)的问题;现在无法识别的格式同样立即抛出异常,与 SetTaskControlSettings 的校验标准保持一致
  • 修复:Only 型(指定时间)与 Cyclicity 型(周期性)配置为正则通过但 DateTime 无法解析的值(如 2010-02-30 10:00:00、*-02-30 10:00:00)时静默永不执行的问题;现在赋值时即抛出异常,VerifyTimeSettingValue 也能正确识别
  • 修复:任务因配置无法解析而未调度时界面只显示"停止"、无任何原因提示的问题;现在 Tips 会显示"时间配置无效/无法解析,任务不会执行"(周期性配置含通配符,合法性随时间变化,按当前时间实际判断)
  • 修复:ExecuteTask 手动触发配置无效的任务后,Tips 显示"任务已完成,不再执行"造成误导的问题;现在不会执行并提示"时间配置无效,无法执行"
  • 优化:周期性配置的"通配符替换 + 解析"提取为共用逻辑,设置期校验与调度期解析走同一实现,消除两处分叉;顺带删除未使用的 minTimeIndex 变量与冗余嵌套判断
  • 优化:任务模块日志统一经 TaskLogHelper 安全包装。LogManage 依赖的 appsettings.json 缺失时(控制台宿主、测试宿主等)日志基础设施不可用,原先 catch 块中的日志调用会变成新的抛异常点,杀死调度循环;现在日志故障不再影响任务注册与调度
  • 优化:修正 V1.7.1 关于预解析缓存的描述(周期性任务每秒仍会做一次时间解析,仅省去重复 Split)
  • 已知限制:LogManage 静态初始化强制要求 appsettings.json 存在(源自 Liwendong.Utility.ConfigHelper),无配置文件的宿主中任务日志不可用(已兜底不影响调度),后续需在 Utility 侧将配置文件改为可选

V1.7.1 (2026-10-15)

  • 修复:立即执行(AsyncExecutionTasksImmediately)不更新任务状态的问题,现在与周期执行共用同一状态机(执行中显示 Running、完成后正常收尾并更新提示),Only 型任务执行后正确置为停止
  • 修复:GetTaskViewModels 锁外逐字段读取任务状态可能读到不一致数据的问题,改为锁内一次性快照
  • 修复:IsStop、Sleep 无跨线程可见性保证的问题(改用 volatile 字段)
  • 修复:Setting 直接赋非法时间间隔(负数、0、超大值)时静默不调度、难以排查的问题,现在赋值时立即抛出异常提示
  • 修复:CustomTask/AddTaskControlItem 在锁内构造任务项、非法配置抛异常时可能留下半成品的问题(构造移出锁,先构造后统一添加)
  • 优化:调度热路径预解析 Setting 并缓存结构化结果,间隔/固定时间任务每秒轮询不再重复解析,周期性任务不再重复 Split,显著降低临时分配
  • 优化:任务类型发现改为只扫描当前已加载程序集,不再扫盘 Assembly.Load 输出目录下所有 DLL 并实例化,消除第三方 DLL 被自动加载执行的侧加载攻击面(安全加固)
  • 优化:删除周期性调度中的冗余排序与 Enable setter 的冗余嵌套锁
  • 新增:TaskHelper.StopAllTask() 优雅停机入口,一次性停止全部任务调度循环
  • 新增:TaskHelper.RefreshTaskCache() 任务类型缓存刷新入口,支持插件式后加载程序集场景
  • 注意:任务类型自动发现范围由"输出目录全部 DLL"收窄为"当前已加载程序集",主程序集与已引用程序集中的任务不受影响;若任务位于未被引用的独立 DLL,需在调用 CustomTask() 前先加载该程序集或调用 RefreshTaskCache()
  • 已知限制:调度全程使用本地时间(DateTime.Now),夏令时切换日固定时间类任务可能漏跑或重复执行一次,暂未处理

V1.7.*

  • 新增 .NET 8.0(net8.0)目标框架支持
  • 时间校验正则增加 5 秒匹配超时(matchTimeout),防止恶意输入触发回溯灾难(ReDoS)
  • 破坏性变更:TaskStatus 枚举更名为 TaskRunState,消除与 System.Threading.Tasks.TaskStatus 的重名冲突; 受影响的公开成员:TaskControlItem.State、TaskViewModel.State、TaskHelper 返回的视图模型、NotExecutionFunction 委托参数。 使用方全局将 TaskStatus(任务模块)替换为 TaskRunState 即可;按数字序列化(StateId)不受影响

V1.6.*

  • 修复:任务执行方法抛出异常会导致任务永久停摆且无任何日志的问题,现在异常会被捕获记录并正常收尾,任务按周期继续调度
  • 修复:任务禁用后重新启用无法恢复调度的问题(调度循环改为只在任务被移除时退出)
  • 修复:CustomTask 重复调用会重复添加任务并重复启动调度循环的问题(同名任务自动去重,只对新增任务启动调度)
  • 修复:并发调用 ExecuteTask/GetTaskViewModels 可能抛出集合遍历异常或空引用的问题(统一加锁并改用单次查找)
  • 修复:GetTaskViewModels 在任务项 Item 为空时抛出空引用的问题
  • 修复:时间间隔配置为负数或超大数字导致的任务风暴、DateTime 溢出、任务列表接口异常的问题(间隔必须大于 0 且不超过 10 年)
  • 修复:内存任务创建时任务 ID 去重判断字段用错(TasksId 误比 TasksKeys)的问题
  • 修复:时间解析依赖当前区域设置的问题(统一使用 InvariantCulture);部分时间正则笔误([0,1] 字符类、缺失结尾锚、小数点未转义)
  • 修复:AsyncExecutionTasks(Action) 由 async void 改为返回 Task,避免未处理异常导致进程崩溃
  • 优化:任务执行增加互斥,立即执行与周期执行、多个调度循环之间不再可能并发重复执行同一任务
  • 优化:NotExecutionFunction 仅在状态或提示变化时回调,不再每秒重复触发
  • 优化:调度轮询间隔自适应任务的 Sleep 配置(上限 1 秒),Sleep 小于 1 秒时不再错过执行窗口
  • 优化:任务类型反射扫描结果进程内缓存,避免每次添加任务都全量扫描并实例化所有任务类型
  • 优化:时间设置校验细化,空值与非法间隔会明确抛出异常提示;任务执行异常不再静默丢失
  • 注意:AsyncExecutionTasks(Action fun) 返回类型由 void 变更为 Task(二进制不兼容,重新编译即可);时间设置为空字符串时 SetTaskControlSettings 现在会抛出异常(原为静默清空配置)

V1.5.*

  • 新增周期性时间配置

V1.4.*

  • 调整了依赖项
  • 新增了时间间隔值的验证,防止配置错误引发任务异常
  • 新增给出当前任务执行的试图模型
  • 任务执行方式调整为只能是异步执行
  • 调整添加任务,如果任务已经存在则不进行任何操作

V1.2.0

  • 新增任务可以直接调用

使用方法

新增任务

 var customerTask = new TaskControlManage()
  {
      TaskName = "任务名",
      Item = new TaskControlItem()
      {
          Enable = [true|false], //是否启用
          Setting = "300", // 时间设置
          ExecutionFunction = null, // 执行方法
      }
  };
  TaskHelper.AddTaskControlItem(customerTask);

移除任务

TaskHelper.RemoveTaskControlItem("任务编号");

更改周期配置

TaskHelper.SetTaskControlSettings("任务编号", "周期配置值");

更改任务启动状态

 TaskHelper.SetTaskControlStatus("任务编号", [true|false]);

手动执行 / 批量停止 / 刷新缓存

 TaskHelper.ExecuteTask("任务编号");   // 手动触发一次;不受启用状态影响,配置无效时不执行并给出提示
 TaskHelper.StopAllTask();             // 停止全部任务的调度循环并禁用任务;不等待正在执行的任务完成
 TaskHelper.RefreshTaskCache();        // 刷新任务类型缓存;需再次调用 CustomTask() 才会注册新发现的任务

周期配置值

  • 纯数字: 表示间隔多少秒,例如 300,表示间隔5分钟(必须大于 0 且不超过 10 年的秒数)
  • "小时:分钟:秒钟" : 表示每小时的指定时间执行。
  • "年-月-日 小时:分钟:秒钟": 表示指定时间执行
  • "年-月-日": 表示指定时间日期的0点0分执行
  • 【V1.5 以上版本支持】周期性任意执行: 完整的时间格式【yyyy-MM-dd HH:mm:ss】中,除了ss不能使用 * 号来代替,其他都可以用 * 来代替,表示任意值(目前不支持秒级查询)

内存任务(MemoryTasksManage)

var manage = new MemoryTasksManage();
// 创建并运行。回调契约:action 必须对每个执行步骤调用 completed(tasksId)(即 CompletedMemoryTasks),
// 否则任务会一直停留在运行中状态直到缓存过期(默认 1 小时);action 抛出异常时自动转为出错状态
var taskId = manage.RunMemoryTasks(new MemoryTaskItem
{
    TasksKeys = "业务关键字",   // 同一关键字在运行中期间不能重复创建
    TasksTitle = "标题",
    Total = 3,                  // 需要完成 3 步
}, (id, completed) =>
{
    completed(id);  // 每完成一步调用一次
});
// 查询与终态
manage.ExistsMemoryTasksByKey("业务关键字");  // 是否存在运行中的任务(O(1) 索引判断)
manage.GetMemoryTasks();                      // 全部任务(Redis 下为前缀扫描,勿高频调用)
manage.CompletedMemoryTasks(taskId);          // 手动完成一步
manage.ErrorMemoryTasks(taskId, "出错原因");   // 手动标记出错

配置项:MemoryTasksCacheSecond(缓存秒数,默认 3600)、MemoryTasksCacheKeyPrefix(缓存 key 前缀,默认 liwendong:memorytasksmanage:)。 多节点部署下的原子性限制见上方"已知限制"。

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp3.1 is compatible. 
.NET Framework net451 is compatible.  net452 was computed.  net46 is compatible.  net461 was computed.  net462 was computed.  net463 was computed.  net47 is compatible.  net471 was computed.  net472 was computed.  net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.1 103 10/20/2026
1.5.0 539 12/9/2025
1.4.7 259 11/24/2025
1.4.6 283 11/22/2025
1.4.5 270 11/22/2025
1.4.4 281 11/22/2025
1.4.3 465 11/20/2025
1.4.2 470 11/20/2025
1.4.1 473 11/20/2025
1.4.0 265 9/1/2025
1.3.1 256 8/13/2025
1.3.0 209 7/29/2025
1.2.2 407 10/17/2023
1.1.0 631 7/18/2022
1.0.0 553 6/18/2021