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

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.lryc.cn/news/598247.html

相关文章:

  • Flink-1.19.0源码详解7-Flink集群端调度
  • ubuntu安装teams解决方法
  • 大模型回复数据标注优化方案
  • 系统架构师:系统安全与分析-思维导图
  • AIRIOT智慧选煤厂管理解决方案
  • 家政小程序系统开发:开启智慧家政新时代
  • Nginx 信创版本源码升级 1.22.1 升级到1.28.0
  • SSE与Websocket有什么区别?
  • uniapp nvue开发App 横竖屏切换丢失上下文导致 setTimeout和clearTimeout报错
  • 全面解析 CSS Flex 布局:从入门到精通的所有属性详解
  • 深入掌握CSS Grid布局:每个属性详解与实战示例
  • k8s通过NUMA亲和分配GPU和VF接口
  • DeepSeek-R1+豆包迭代一次完成中国象棋游戏
  • 二、计算机网络技术——第6章:应用层
  • rk3588开发板使用硬件编码处理视频
  • 国产数据库拐点已至:电科金仓用“融合+AI”重新定义下一代数据底座
  • C++ 23种设计模式-工厂模式
  • (实用攻略)Linux操作系统(一)
  • 输电线路微气象在线监测装置:保障电网安全的科技屏障
  • 【基础】go基础学习笔记
  • 进阶向:基于Python的本地文件内容搜索工具
  • SpringCloud【Sentinel】
  • 【C++】类和对象(1)
  • CDH yarn 重启后RM两个备
  • Compose 适配 - 键鼠模式
  • 图像认知与OpenCV——图像预处理2
  • 到底可不可以用jion?jion如何优化?
  • 【学习】数字化车间与智能工厂如何推进制造业转型
  • MIT线性代数02_矩阵消元
  • 云祺容灾备份系统AWS S3对象存储备份与恢复实操手册