当前位置: 首页 > ops >正文

C#_定时器_解析

问题一:这里加lock是啥意思?它的原理是, 为什么可以锁住?

private readonly Timer _timer;/// <summary>/// 构造函数中初始化定时器/// </summary>public FtpTransferService(){// 初始化定时器_timer = new Timer(_intervalMinutes * 60 * 1000);_timer.Elapsed += OnTimerElapsed;//绑定定时触发的事件_timer.AutoReset = true;//定时器的属性,控制是否在每次间隔后重复触发事件}/// <summary>/// 定时器触发事件 - 增加锁防止并发/// </summary>private void OnTimerElapsed(object sender, ElapsedEventArgs e){lock (_lock){try{WriteLog($"开始执行文件转移任务({DateTime.Now:yyyy-MM-dd HH:mm:ss})");// 遍历所有目录(包括非最内层)并复制结构TransferAllDirectories(_sourceRootPath);WriteLog($"文件转移任务执行完成({DateTime.Now:yyyy-MM-dd HH:mm:ss})");}catch (Exception ex){WriteLog($"任务执行异常:{ex.Message}\r\n堆栈:{ex.StackTrace}", isError: true);}}}

在这段代码中,lock (_lock) 的作用是防止定时器事件被并发执行,确保 OnTimerElapsed 方法中的逻辑在同一时间只能被一个线程执行。

为什么需要加 lock

System.Timers.TimerElapsed 事件默认由线程池线程触发。如果定时任务(如代码中的 TransferAllDirectories)的执行时间超过了定时器的间隔时间,就会出现以下问题:

  • 第一个任务还没执行完,第二个定时器事件就被触发,导致两个线程同时执行 OnTimerElapsed 中的逻辑。
  • 这可能引发资源竞争(如同时操作同一文件/目录),导致数据错乱或异常。

lock 的作用就是保证同一时间只有一个线程能进入被锁定的代码块,避免这种并发冲突。

lock 的工作原理

lock 本质上是基于 .NET 的** Monitor 同步机制**实现的,其核心逻辑可以简化为:

  1. 当线程进入 lock (_lock) 代码块时,会尝试获取 _lock 对象的「独占锁」。
  2. 如果锁未被占用,当前线程会获取锁并执行代码块。
  3. 如果锁已被其他线程占用,当前线程会进入阻塞状态,等待锁被释放。
  4. 当线程执行完代码块后,会自动释放锁,让其他等待的线程可以获取锁并执行。

为什么用 _lock 这个对象?

lock 需要一个「同步对象」(这里是 _lock),它的作用是:

  • 作为锁的「标识」,不同的同步对象对应不同的锁。
  • 必须是引用类型(如 object),因为值类型会被装箱,每次 lock 都会创建新对象,导致锁失效。
  • 通常定义为 private readonly,确保:
    • 外部代码无法访问该对象(避免外部误操作导致锁失控)。
    • 对象引用不会被修改(保证始终用同一个对象作为锁标识)。

这段代码中 lock 的实际效果

假设定时器间隔为 5 分钟,但 TransferAllDirectories 某次执行花了 7 分钟:

  • 第一个线程在第 0 分钟进入代码块,获取 _lock 锁。
  • 第 5 分钟时,定时器再次触发 Elapsed 事件,第二个线程尝试进入代码块,但此时锁被第一个线程占用,只能等待。
  • 第 7 分钟,第一个线程执行完并释放锁,第二个线程立即获取锁并开始执行。

这样就避免了两个线程同时执行文件转移逻辑,保证了操作的安全性。

注意点

  • lock 会导致等待的线程阻塞,因此被锁定的代码块应尽量简短,避免长时间占用锁影响性能。
  • 不要用 stringthis 作为同步对象:string 有字符串驻留机制,可能导致不同地方的锁冲突;this 可能被外部代码访问,存在安全风险。
  • 此代码中 _timer.AutoReset = true 表示定时器会重复触发,结合 lock 可以完美解决「任务执行时间超过间隔」的并发问题。
http://www.xdnf.cn/news/16120.html

相关文章:

  • 光猫配置DMZ到路由器
  • 20-ospf技术
  • Kafka入门指南:从零开始掌握分布式消息队列
  • AI 在金融:重塑金融服务的智能革命
  • Linux中虚拟地址和物理地址互相转化之页表的详解
  • 微算法科技(NASDAQ: MLGO)研究量子信息递归优化(QIRO)算法,为组合优化问题拓展解决新思路
  • C++图论全面解析:从基础概念到算法实践
  • [iOS开发工具] 【iOS14以及以下】cydia商店按键精灵iOS新版V2.X安装教程
  • 教程:如何通过代理服务在国内高效使用 Claude API 并集成到 VSCode
  • 【SpringAI实战】FunctionCalling实现企业级自定义智能客服
  • 完整指南:使用Apache htpasswd为Chronograf配置基础认证及功能详解
  • 进阶向:基于Python的本地文件内容搜索工具
  • 机器学习概述与 KNN 算法详解
  • 亚纳米级检测!潜望式棱镜的“检测密码”,决定手机远景清晰度
  • 两台电脑连接交换机,使用其中一台电脑的网络上网(NAT转发)
  • 单片机的硬件结构
  • 使用Docker+Nginx部署电商平台项目(服务端+管理端+商城)
  • C++11之可变参数模板
  • 快速启用 JMeter(macOS Automator 创建 JMeter 脚本)
  • Java从入门到精通!第十三天(IO 流)
  • 医疗AI轻量化部署方案的深度梳理与优化路径判研
  • 【Luogu】每日一题——Day12. P3149 排序 (树状数组 + 逆序对)
  • 阿里云ECS坑之dnf-makecache系统软件更新检测服务
  • 【C++】类和对象(中)构造函数、析构函数
  • vue3路由详解
  • ubuntulinux快捷键
  • 第1章第2章笔记
  • 大模型【进阶】(四)QWen模型架构的解读
  • 前端跨域请求原理及实践
  • 顺丰面试提到的一个算法题