System.Timers.Timer与System.Threading.Timer的区别

636

最近我一直在研究可能的计时器,System.Threading.TimerSystem.Timers.Timer是我觉得必须要用的(因为它们支持线程池)。

我正在制作一个游戏,打算使用各种不同时间间隔的事件等。

哪一个计时器是最好的呢?

9个回答

409

本文提供了一个相当全面的解释:

"比较.NET Framework类库中的计时器类" - 同样可以作为.chm文件下载。

两者之间的具体区别在于,System.Timers.Timer适用于多线程应用程序,因此通过其SynchronizationObject属性是线程安全的,而具有讽刺意味的是,System.Threading.Timer默认情况下不是线程安全的。

我认为就定时间隔的大小而言,这两者之间没有区别。


82
我认为这段摘录很有启发性:“与System.Windows.Forms.Timer不同,System.Timers.Timer类默认情况下将从公共语言运行时(CLR)线程池获得的工作线程调用计时器事件处理程序。[...] System.Timers.Timer类提供了一种简单的方法来解决这个问题——它公开了一个公共的SynchronizingObject属性。将此属性设置为Windows窗体的一个实例(或者Windows窗体上的控件)将确保Elapsed事件处理程序中的代码在实例化SynchronizingObject的相同线程上运行。” - mico
8
根据Threading.Timer的MSDN文章中的“线程安全”部分,它是完全线程安全的。 - Pieter
77
「System.Threading.Timer」与通过线程池获取的线程一样“具有讽刺意味”的不是线程安全的,就像「System.Threading.Thread」一样。尽管这些类不会手把手地管理使用「lock」关键字,但这并不意味着这些类不是线程安全的。如果这样说,那么「System.Threading.Thread」也可以说不是线程安全的,因为这同样是正确的。 - Kirk Woll
11
System.Timer.Timer 的间隔只能是 Int32。 System.Threading.Timer 的间隔可以达到 Int64。 - Brent
37
很不幸,这个明显误导人的答案被接受并且得到高票。答案本身唯一的实际声明是错误的。SynchronizingObject 并不使得定时器对象本身线程安全,它只确保在特定线程中调用处理定时器事件的代码(如果你适当地设置了该属性)。如文档所述,定时器对象本身仍然不能保证是线程安全的。另一方面,System.Threading.Timer 对象的文档明确指出它是线程安全的。 - Peter Duniho
显示剩余5条评论

217

System.Threading.Timer 是一个普通的定时器。它在线程池线程上调用您(来自工作线程池)。

System.Timers.Timer 是一个包装了System.Threading.TimerSystem.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,或者每个人都意味着:

  1. 当您使用定时器时很容易编写竞态条件。例如,参见此问题:Timer (System.Threading) thread safety

  2. 定时器通知的重入性,其中您的计时器事件可以在您完成处理第一个事件之前触发并回调您的第二个时间。例如,请参见此问题:Thread-safe execution using System.Threading.Timer and Monitor


3
确实,System.Timers.Timer 内部使用了 System.Threading.Timer。请参考源代码 - stomy
在.NET / .NET Core中:https://source.dot.net/#System.ComponentModel.TypeConverter/System/Timers/Timer.cs - EricBDev

126
在他的书《CLR Via C#》中,Jeff Ritcher 不建议使用 System.Timers.Timer。该计时器派生自 System.ComponentModel.Component,因此可以在 Visual Studio 的设计界面中使用。因此,如果您想在设计界面上使用计时器,则该计时器才有用。
他更喜欢使用 System.Threading.Timer 在线程池线程上执行后台任务。

45
它可以在设计表面中使用 - 这并不意味着必须这样做,而且不这样做也没有任何不利影响。阅读之前回答此问题的文章,Timers.Timer似乎比Threading.Timer更可取。 - Stephen Drew
6
好的,什么是更好取决于上下文,对吧?据我理解,System.Threading.Timer会在一个来自线程池的新工作线程上执行传入的回调函数。这也是它不一定是线程安全的原因之一,这有点说得通。因此,理论上来说,你不必自己费力地创建一个工作线程,因为这个计时器已经为你做了这件事。这似乎非常有用。 - Finster
8
使用 System.Threading.Timer 就像使用线程池或创建自己的线程一样。 当然,这些类不会为您处理同步 - 这是您的工作! 无论是线程池线程、自己的线程还是计时器回调都不会处理锁定 - 您需要在哪个对象上以何种方式和在何种情况下进行锁定,需要良好的判断力,而计时器的线程版本提供了最大的灵活性和细粒度控制。 - Kirk Woll
5
这个答案从一开始就是主观或带有个人意见的,没有提供任何关于为什么Jeff Ritcher更喜欢System.Threading.Timer的具体信息。 - Brian Ogden
我猜你们在谈论Jeffrey Richter? - Martin Maat

66

来自微软对此的说明(请参见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组件,定期进行异步或同步的Web页面回发。
有趣的是,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.Threading.Timer 而非 System.Timers.Timer编辑注释2018-11-15:我不得不更改我的答案,因为有关.NET Core 1.0的旧信息已经过期。

2
看起来是真的:https://github.com/npgsql/npgsql/issues/471#issuecomment-94104090 - Marco Sulla
@Taegost,它的使用也是受限制的--请参考MSDN https://msdn.microsoft.com/zh-cn/library/system.timers.timer(v=vs.110).aspx 并阅读关于平台可用性的说明。 - astrowalker
@astrowalker - 感谢您,当我发表评论时,这个答案几乎没有任何细节。由于现在它已经有了我所询问的细节,我删除了我的评论。 - Taegost
1
System.Timers.Timer现在支持.NET Standard 2.0和.NET Core 2.0及以上版本。https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer(滚动到文章末尾) - Lee Grissom
根据这个答案,还有System.Windows.Threading.DispatcherTimer - SpeedCoder5

52

上面未提到的一个重要区别可能会让你陷入困境,即 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);

1
我最近遇到了Timers.Timer的问题,这让我很痛苦...有什么想法可以用Threading.Timer重写吗?https://dev59.com/-Z7ha4cB1Zd3GeqPeyXU - Tez Wingfield
11
源代码中有一个空的catch。请查看此处System.Timers.Timer源代码 - stomy
哎呀,为什么微软的程序员不能做到完全一样的事情呢? - xmedeko
8
这个例子没有解释一个如何吞噬异常而另一个不会。能否有人填写细节? - sean

24
我找到了一篇简短的比较文章,来源于MSDN
.NET Framework类库包括四个名为 Timer 的类,每个类都提供不同的功能:
- System.Timers.Timer:按照规律间隔时间触发事件并执行一个或多个事件接收器中的代码。该类旨在用作多线程环境中的服务器端或服务组件;它没有用户界面,在运行时不可见。 - System.Threading.Timer:在一个线程池线程上以规律间隔时间执行单个回调方法。定时器实例化时定义回调方法,且无法更改。与System.Timers.Timer类似,该类旨在用作多线程环境中的服务器端或服务组件;它没有用户界面,在运行时不可见。 - System.Windows.Forms.Timer:是一个Windows Forms组件,按规律间隔时间触发事件并执行一个或多个事件接收器中的代码。该组件没有用户界面,设计用于单线程环境。 - System.Web.UI.Timer:是一个ASP.NET组件,定期执行异步或同步的网页后台处理。

3
从MSDN上来看:System.Threading.Timer是一个简单、轻量级的计时器,它使用回调方法并由线程池线程服务。不推荐在Windows Forms中使用它,因为它的回调不会发生在用户界面线程上。对于Windows Forms,System.Windows.Forms.Timer是更好的选择。对于基于服务器的定时器功能,您可能考虑使用System.Timers.Timer,它触发事件并具有其他特性。

来源


3
如其他人提到的MS Docs链接所述,System.Timers.TimerSystem.Threading.Timer之间的一个主要区别是:System.Threading.Timer执行单个回调方法,该方法只有在定义时才会被定义,而System.Timers.Timer则对事件做出反应,因此支持多个订阅者,也可以删除它们。
正如上面提到的,System.Timers.Timer在内部使用System.Threading.Timer,例如Enable=false将销毁内部计时器,并在Enable=true / Start()上重新创建它:https://source.dot.net/#System.ComponentModel.TypeConverter/System/Timers/Timer.cs

1
这两个类在功能上是等效的,不同之处在于System.Timers.Timer有一个选项可以通过设置SynchronizingObject来通过ISynchronizeInvoke调用所有定时器到期回调。否则,两个定时器都会在线程池线程上调用到期回调。
当您将System.Timers.Timer拖放到Windows Forms设计表面时,Visual Studio会将SynchronizingObject设置为窗体对象,这会导致所有到期回调在UI线程上调用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接