最近我一直在研究可能的计时器,System.Threading.Timer
和System.Timers.Timer
是我觉得必须要用的(因为它们支持线程池)。
我正在制作一个游戏,打算使用各种不同时间间隔的事件等。
哪一个计时器是最好的呢?
最近我一直在研究可能的计时器,System.Threading.Timer
和System.Timers.Timer
是我觉得必须要用的(因为它们支持线程池)。
我正在制作一个游戏,打算使用各种不同时间间隔的事件等。
哪一个计时器是最好的呢?
本文提供了一个相当全面的解释:
"比较.NET Framework类库中的计时器类" - 同样可以作为.chm文件下载。
两者之间的具体区别在于,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
定时器通知的重入性,其中您的计时器事件可以在您完成处理第一个事件之前触发并回调您的第二个时间。例如,请参见此问题:Thread-safe execution using System.Threading.Timer and Monitor
System.Threading.Timer
就像使用线程池或创建自己的线程一样。 当然,这些类不会为您处理同步 - 这是您的工作! 无论是线程池线程、自己的线程还是计时器回调都不会处理锁定 - 您需要在哪个对象上以何种方式和在何种情况下进行锁定,需要良好的判断力,而计时器的线程版本提供了最大的灵活性和细粒度控制。 - Kirk Woll来自微软对此的说明(请参见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 进行切换,这可能就是它回归的原因。System.Threading.Timer
。System.Threading.Timer
而非 System.Timers.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);
System.Threading.Timer
是一个简单、轻量级的计时器,它使用回调方法并由线程池线程服务。不推荐在Windows Forms中使用它,因为它的回调不会发生在用户界面线程上。对于Windows Forms,System.Windows.Forms.Timer
是更好的选择。对于基于服务器的定时器功能,您可能考虑使用System.Timers.Timer
,它触发事件并具有其他特性。
System.Timers.Timer
和System.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。System.Timers.Timer
有一个选项可以通过设置SynchronizingObject来通过ISynchronizeInvoke调用所有定时器到期回调。否则,两个定时器都会在线程池线程上调用到期回调。System.Timers.Timer
拖放到Windows Forms设计表面时,Visual Studio会将SynchronizingObject设置为窗体对象,这会导致所有到期回调在UI线程上调用。
Threading.Timer
的MSDN文章中的“线程安全”部分,它是完全线程安全的。 - PieterSynchronizingObject
并不使得定时器对象本身线程安全,它只确保在特定线程中调用处理定时器事件的代码(如果你适当地设置了该属性)。如文档所述,定时器对象本身仍然不能保证是线程安全的。另一方面,System.Threading.Timer
对象的文档明确指出它是线程安全的。 - Peter Duniho