我最近一直在检查一些可能的计时器,System.Threading.Timer
和 System.Timers.Timer
对我来说是必要的(因为它们支持线程池)。
我正在制作一个游戏,我计划使用所有类型的事件,以不同的间隔等。
哪个是最好的?
这篇文章提供了一个相当全面的解释:
“Comparing the Timer Classes in the .NET Framework Class Library” - 也可用 as a .chm file
具体的区别似乎是 System.Timers.Timer
面向多线程应用程序,因此通过其 SynchronizationObject
属性是线程安全的,而 System.Threading.Timer
具有讽刺意味的是开箱即用不是线程安全的。
我不相信两者之间有区别,因为它与你的间隔有多小有关。
System.Threading.Timer
是一个普通的计时器。它会在线程池线程(来自工作池)上调用您。
System.Timers.Timer
是包装 System.Threading.Timer
的 System.ComponentModel.Component
,并提供一些用于在特定线程上调度的附加功能。
System.Windows.Forms.Timer
改为包装原生 message-only-HWND 并使用 Window Timers 在该 HWND 消息循环中引发事件。
如果您的应用没有 UI,并且您希望尽可能地使用最轻量级和通用的 .Net 计时器(因为您乐于解决自己的线程/调度),那么 System.Threading.Timer
与框架中的一样好.
我不完全清楚 System.Threading.Timer
所谓的“非线程安全”问题是什么。也许它与这个问题中所问的相同:Thread-safety of System.Timers.Timer vs System.Threading.Timer,或者也许每个人都只是意味着:
使用计时器时很容易编写竞争条件。例如看这个问题:Timer (System.Threading) thread safety re-entancy of timer notifications,在你完成第一个事件处理之前,你的定时器事件可以触发并再次回调你。例如看到这个问题:Thread-safe execution using System.Threading.Timer and Monitor
在他的《CLR Via C#》一书中,Jeff Ritcher 不鼓励使用 System.Timers.Timer
,这个计时器是从 System.ComponentModel.Component
派生的,允许它用于 Visual 的设计表面工作室。因此,仅当您想要在设计表面上使用计时器时才有用。
他更喜欢将 System.Threading.Timer
用于线程池线程上的后台任务。
System.Threading.Timer
类似于使用线程池或创建您自己的线程。 当然这些类不会为你处理同步——那是你的工作!线程池线程、您自己的线程和计时器回调都不会处理锁定——在什么对象上、以什么方式以及在什么情况下您需要锁定需要良好的判断,并且计时器的线程版本为您提供了最大的灵活性和粒度。
Microsoft 提供的相关信息(请参阅 Remarks on MSDN):
System.Timers.Timer,它触发一个事件并定期在一个或多个事件接收器中执行代码。该类旨在用作多线程环境中的基于服务器或服务组件;它没有用户界面,在运行时不可见。 System.Threading.Timer,它定期在线程池线程上执行单个回调方法。回调方法是在定时器实例化时定义的,不能更改。与 System.Timers.Timer 类一样,此类旨在用作多线程环境中的基于服务器或服务组件;它没有用户界面,在运行时不可见。 System.Windows.Forms.Timer(仅限 .NET Framework),一种 Windows 窗体组件,可触发事件并定期在一个或多个事件接收器中执行代码。该组件没有用户界面,设计用于单线程环境;它在 UI 线程上执行。 System.Web.UI.Timer(仅限 .NET Framework),一个定期执行异步或同步网页回发的 ASP.NET 组件。
有趣的是,System.Timers.Timer
在 .NET Core 1.0 中已被弃用,但在 .NET Core 2.0 (/.NET Standard 2.0) 中再次实现。 .NET Standard 2.0 的目标是尽可能轻松地从 .NET Framework 切换,这可能是它回归的原因。
当它被弃用时,.NET Portability Analyzer Visual Studio Add-In 建议改用 System.Threading.Timer
。
看起来微软在 System.Timers.Timer
之前偏爱 System.Threading.Timer
。
编辑说明 2018-11-15:我不得不更改答案,因为有关 .NET Core 1.0 的旧信息不再有效。
上面没有提到的一个重要区别可能会让您感到困惑,那就是 System.Timers.Timer
会默默地吞下异常,而 System.Threading.Timer
不会。
例如:
var timer = new System.Timers.Timer { AutoReset = false };
timer.Elapsed += (sender, args) =>
{
var z = 0;
var i = 1 / z;
};
timer.Start();
对比
var timer = new System.Threading.Timer(x =>
{
var z = 0;
var i = 1 / z;
}, null, 0, Timeout.Infinite);
我从 MSDN 中找到了一个简短的比较
.NET Framework 类库包括四个名为 Timer 的类,每个类都提供不同的功能: System.Timers.Timer,它触发一个事件并定期在一个或多个事件接收器中执行代码。该类旨在用作多线程环境中的基于服务器或服务组件;它没有用户界面,在运行时不可见。 System.Threading.Timer,它定期在线程池线程上执行单个回调方法。回调方法是在定时器实例化时定义的,不能更改。与 System.Timers.Timer 类一样,此类旨在用作多线程环境中的基于服务器或服务组件;它没有用户界面,在运行时不可见。 System.Windows.Forms.Timer,一种 Windows 窗体组件,它触发一个事件并定期在一个或多个事件接收器中执行代码。该组件没有用户界面,设计用于单线程环境。 System.Web.UI.Timer,一个定期执行异步或同步网页回发的 ASP.NET 组件。
来自 MSDN:System.Threading.Timer
是一个简单的轻量级计时器,它使用回调方法并由线程池线程提供服务。不建议与 Windows 窗体一起使用,因为它的回调不会发生在用户界面线程上。 System.Windows.Forms.Timer
是与 Windows 窗体一起使用的更好选择。对于基于服务器的计时器功能,您可以考虑使用 System.Timers.Timer
,它会引发事件并具有附加功能。
这两个类在功能上是等效的,除了 System.Timers.Timer
可以通过设置 SynchronizingObject 来通过 ISynchronizeInvoke 调用其所有计时器到期回调。否则,两个计时器都会在线程池线程上调用到期回调。
当您将 System.Timers.Timer
拖到 Windows 窗体设计图面上时,Visual Studio 会将 SynchronizingObject 设置为表单对象,这会导致在 UI 线程上调用所有到期回调。
正如其他人提到的 MS Docs 的链接,System.Timers.Timer
和 System.Threading.Timer
之间的一个主要区别是 System.Threading.Timer
执行 single 回调方法定义一次,而 { 2} 对事件做出反应,因此支持多个订阅者,也可以删除。
如上所述,System.Timers.Timer
在内部使用 System.Threading.Timer
,例如 Enable=false 处理内部计时器,并在 Enable=true / Start() 上重新创建它:https://source.dot.net/#System.ComponentModel.TypeConverter/System/Timers/Timer.cs
不定期副业成功案例分享
Threading.Timer
的 MSDN article 中的线程安全部分,它是完全线程安全的......System.Threading.Timer
与System.Threading.Thread
和通过池获得的线程一样“具有讽刺意味”而不是线程安全的。仅仅因为这些类不牵你手并管理lock
关键字本身的使用并不意味着这些类不是线程安全的。您不妨说System.Threading.Thread
不是线程安全的,因为它完全一样。SynchronizingObject
不会使计时器对象本身成为线程安全的。它只是确保在特定线程中调用处理计时器事件的您的代码(如果您适当地设置了该属性)。正如文档中明确指出的那样,计时器对象本身仍然不保证是线程安全的。另一方面,System.Threading.Timer
对象 特别记录为线程安全的。