C# 定时创建Task前如何校验指定参数的MyProcedure任务是否已存在
C# 同参数运行中Task去重实现方案
实现思路
使用线程安全的ConcurrentDictionary存储参数和对应运行中Task的映射关系,每次定时器触发时先校验当前参数是否已在字典中:
- 不存在则将参数加入字典,创建并启动对应任务,任务执行结束(含异常退出)后从字典移除该参数记录
- 已存在则直接跳过本次创建操作
实现代码
首先定义全局的跟踪字典,键的类型可根据你的myParameter实际类型调整:
// 线程安全字典,key为MyProcedure的入参myParameter,value为对应运行中的Task实例 private readonly ConcurrentDictionary<object, Task> _runningTaskMap = new ConcurrentDictionary<object, Task>();
定时器触发时的业务逻辑:
object currentParam = myParameter; // 尝试添加参数到字典,返回false说明同参数任务正在运行,直接跳过 if (_runningTaskMap.TryAdd(currentParam, null)) { try { // 创建并启动任务,Task.Run为C#推荐的线程池任务启动方式 Task execTask = Task.Run(() => { try { MyProcedure(currentParam); } finally { // 任务无论执行成功还是抛出异常,都要移除字典中的记录 _runningTaskMap.TryRemove(currentParam, out _); } }); // 更新字典中存储的任务实例 _runningTaskMap[currentParam] = execTask; } catch { // 任务启动异常时清理字典记录,避免后续同参数任务永远无法创建 _runningTaskMap.TryRemove(currentParam, out _); throw; } }
注意事项
- 如果
myParameter是自定义类型,需要正确重写Equals和GetHashCode方法,保证相同业务含义的参数会被判定为同一个键 - 如果你需要等待已有任务执行完成,可在
TryAdd返回false时,通过_runningTaskMap.TryGetValue(currentParam, out var existTask)拿到已有任务实例,再调用await existTask或existTask.Wait()实现等待逻辑,定时器场景下一般不需要阻塞当前线程,跳过本次执行即可 - 不推荐直接使用
new Task().Start()的方式启动任务,Task.Run会直接从线程池调度线程执行,性能和可靠性更高
内容的提问来源于stack exchange,提问作者Marcel Macecek
相关产品推荐
相关产品推荐

