0

0

c# System.Threading.Timer 和 System.Timers.Timer 的区别和线程安全

月夜之吻

月夜之吻

发布时间:2026-01-21 10:21:08

|

478人浏览过

|

来源于php中文网

原创

System.Threading.Timer首次回调立即执行,System.Timers.Timer首次触发需等待Interval;两者均使用线程池且不保证线程安全,回调并发执行可能引发竞态;UI更新须显式切换主线程,Dispose()为唯一可靠释放方式。

c# system.threading.timer 和 system.timers.timer 的区别和线程安全

System.Threading.Timer 和 System.Timers.Timer 的触发时机与线程模型差异

两者都用线程池线程执行回调,但触发逻辑根本不同:System.Threading.Timer 默认「立即执行首次回调(可设 dueTime = 0)」,之后按 period 间隔重复;而 System.Timers.Timer 总是「先等一个 Interval 才触发第一次 Elapsed 事件」,哪怕你刚调用 Start()。这意味着如果你需要「启动即干活」,Threading.Timer 更直接,不用额外手动调一次回调。

线程模型上,它们都**不保证线程安全**——回调本身在线程池中并发执行,但二者对「重入」的默认行为不同:

  • System.Threading.Timer:每次回调都是独立的线程池任务,若回调执行慢、且 period 小于执行耗时,会堆积多个并行回调,可能引发竞态(比如同时写同一个 Dictionary
  • System.Timers.Timer:同样不阻塞后续触发,AutoReset = true(默认)时,下一次 Elapsed 会在前一次还没结束时照常触发,也会并发执行

为什么不能直接在回调里更新 UI?怎么安全地切回主线程?

两者回调都在后台线程运行,直接访问 TextBox.TextControl.Invoke 会抛出 InvalidOperationException: “线程间操作无效”。这不是“定时器不安全”,而是 WinForms/WPF 的线程亲和性限制。

安全做法取决于场景:

  • WinForms 中用 System.Timers.Timer:可设置 SynchronizingObject = this(或任意 ISynchronizeInvoke 对象),它会自动把 Elapsed 事件封送到 UI 线程 —— 这是它比 Threading.Timer 唯一方便的地方
  • 通用方案(尤其控制台、服务、或跨平台):用 Task.Run + await Dispatcher.InvokeAsync(...)(WPF)或 this.Invoke((MethodInvoker)delegate { ... })(WinForms)显式切换
  • 千万别在回调里直接 new Form 或 ShowDialog() —— 即使切了线程,模态对话框仍可能卡死消息循环

内存、精度、Dispose:三个最容易被忽略的坑

实测数据显示:System.Threading.Timer 实例几乎零内存分配(Allocated = 0 B),而 System.Timers.Timer 每个实例固定占用约 18 KB 内存(.NET Framework 4.8+)。高频创建/销毁大量定时器时,后者会明显推高 GC 压力。

精度方面:Threading.Timer 首次触发延迟更稳定(实测平均 ~15 ms),Timers.Timer 因事件路由开销,首次延迟波动大(实测达 90 ms 以上)。

千博购物系统.Net
千博购物系统.Net

千博购物系统.Net能够适合不同类型商品,为您提供了一个完整的在线开店解决方案。千博购物系统.Net除了拥有一般网上商店系统所具有的所有功能,还拥有着其它网店系统没有的许多超强功能。千博购物系统.Net适合中小企业和个人快速构建个性化的网上商店。强劲、安全、稳定、易用、免费是它的主要特性。系统由C#及Access/MS SQL开发,是B/S(浏览器/服务器)结构Asp.Net程序。多种独创的技术使

下载

最关键的是资源释放:

  • System.Threading.Timer 必须显式调用 Dispose(),否则回调可能持续执行(即使引用丢失),造成内存泄漏和意外触发
  • System.Timers.Timer 同样必须 Dispose(),且建议配合 Stop() 使用;若只 Stop()Dispose(),内部事件订阅和线程池句柄不会释放
  • 别依赖析构函数 —— 它们都不实现终结器,Dispose() 是唯一可靠方式

选哪个?看这三句话就足够

System.Threading.Timer 当你:需要极致轻量、要精确控制首次触发时机、写后台服务/高性能中间件、能接受回调是纯委托(不带事件语义)。

System.Timers.Timer 当你:正在 WinForms 项目中且想省掉手动线程切换、需要 AutoReset/Enabled 这类状态属性、团队习惯事件编程模型、不介意多那 18 KB。

永远别用它们做耗时操作 —— 无论是读文件、发 HTTP 请求还是复杂计算,都应外包给 Task.Run 并加超时控制;否则线程池饥饿、定时漂移、甚至整个应用卡顿都会找上门。

相关专题

更多
什么是中间件
什么是中间件

中间件是一种软件组件,充当不兼容组件之间的桥梁,提供额外服务,例如集成异构系统、提供常用服务、提高应用程序性能,以及简化应用程序开发。想了解更多中间件的相关内容,可以阅读本专题下面的文章。

178

2024.05.11

Golang 中间件开发与微服务架构
Golang 中间件开发与微服务架构

本专题系统讲解 Golang 在微服务架构中的中间件开发,包括日志处理、限流与熔断、认证与授权、服务监控、API 网关设计等常见中间件功能的实现。通过实战项目,帮助开发者理解如何使用 Go 编写高效、可扩展的中间件组件,并在微服务环境中进行灵活部署与管理。

213

2025.12.18

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

392

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

572

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

392

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

572

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

482

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

482

2023.08.10

Python GraphQL API 开发实战
Python GraphQL API 开发实战

本专题系统讲解 Python 在 GraphQL API 开发中的实际应用,涵盖 GraphQL 基础概念、Schema 设计、Query 与 Mutation 实现、权限控制、分页与性能优化,以及与现有 REST 服务和数据库的整合方式。通过完整示例,帮助学习者掌握 使用 Python 构建高扩展性、前后端协作友好的 GraphQL 接口服务,适用于中大型应用与复杂数据查询场景。

1

2026.01.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号