.NET System.Timers.Timer的原理和使用(开发定时执行程序)

来源:岁月联盟 编辑:exp 时间:2011-10-11

 

概述(来自MSDN)

 

Timer 组件是基于服务器的计时器,它使您能够指定在应用程序中引发Elapsed 事件的周期性间隔。然后可以操控此事件以提供定期处理。例如,假设您有一台关键性服务器,必须每周7 天、每天24 小时都保持运行。可以创建一个使用Timer 的服务,以定期检查服务器并确保系统开启并在运行。如果系统不响应,则该服务可以尝试重新启动服务器或通知管理员。

 

基于服务器的Timer 是为在多线程环境中用于辅助线程而设计的。服务器计时器可以在线程间移动来处理引发的Elapsed 事件,这样就可以比Windows 计时器更精确地按时引发事件。

 

基于Interval 属性的值,Timer 组件引发Elapsed 事件。可以处理该事件以执行所需的处理。例如,假设您有一个联机销售应用程序,它不断向数据库发送销售订单。编译发货指令的服务分批处理订单,而不是分别处理每个订单。可以使用Timer 每30 分钟启动一次批处理。

 

注意

 

当AutoReset设置为false时,Timer只在第一个Interval过后引发一次Elapsed事件。若要保持以Interval时间间隔引发Elapsed 事件,请将AutoReset设置为true。

Elapsed事件在ThreadPool线程上引发。如果Elapsed事件的处理时间比Interval长,在另一个hreadPool线程上将会再次引发此事件。因此,事件处理程序应当是可重入的。

 

注意

 

在一个线程调用Stop 方法或将Enabled 属性设置为false 的同时,可在另一个线程上运行事件处理方法。这可能导致在计时器停止之后引发Elapsed 事件。Stop 方法的示例代码演示了一种避免此争用条件的方法。

如果和用户界面元素(如窗体或控件)一起使用Timer,请将包含有Timer 的窗体或控件赋值给SynchronizingObject 属性,以便将此事件封送到用户界面线程中。Timer 在运行时是不可见的。

 

 

 

几点说明

 

1 private System.Timers.Timer _TestTimerEvent= new Timer();

1   

 

1、默认的周期是0.1秒执行一次;

 

2、AutoReset的初始值为true.

 

3、它的timer机制和System.Threading.Timer 原理是一样的。

 

4、每次周期(Timer)运行一次会新起一个线程。

 

5、如果Elapsed事件的处理时间比Interval长,它每个周期执行都会新起一个线程,这个线程的执行时间不受interval的限定,可以比interval长,因为一个新周期执行,又会新起一个线程,Timer起的线程周期就是事件处理时间。

 

我们来看它的实现代码.(.net framework 提供的).

 

001 //------------------------------------------------------------------------------  

 

002 // <copyright file="Timer.cs" company="Microsoft"> 

 

003 //     Copyright (c) Microsoft Corporation.  All rights reserved. 

 

004 // </copyright> 

 

005 //-----------------------------------------------------------------------------  

 

006   

 

007 namespace System.Timers {  

 

008    

 

009     using System.Runtime.InteropServices; 

 

010     using System.Security;  

 

011     using System.Security.Permissions; 

 

012     using System.Threading; 

 

013     using System.ComponentModel; 

 

014     using System.ComponentModel.Design;  

 

015     using System; 

 

016     using Microsoft.Win32;  

 

017     using Microsoft.Win32.SafeHandles;  

 

018   

 

019     /// <devdoc>  

 

020     ///    <para>Handles recurring events in an application.</para> 

 

021     /// </devdoc> 

 

022     [ 

 

023     DefaultProperty("Interval"),  

 

024     DefaultEvent("Elapsed"), 

 

025     HostProtection(Synchronization=true, ExternalThreading=true)  

 

026     ]  

 

027     public class Timer : Component, ISupportInitialize { 

 

028         private double interval;  

 

029         private bool  enabled; 

 

030         private bool initializing; 

 

031         private bool delayedEnable; 

 

032         private ElapsedEventHandler onIntervalElapsed;  

 

033         private bool autoReset; 

 

034         private ISynchronizeInvoke synchronizingObject;  

 

035         private bool disposed;  

 

036         private System.Threading.Timer timer; 

 

037         private TimerCallback callback;  

 

038         private Object cookie; 

 

039   

 

040         /// <devdoc> 

 

041         /// <para>Initializes a new instance of the <see cref='System.Timers.Timer'/> class, with the properties  

 

042         ///    set to initial values.</para> 

 

043         /// </devdoc>  

 

044         public Timer()  

 

045         : base() { 

 

046             interval = 100;  

 

047             enabled = false; 

 

048             autoReset = true; 

 

049             initializing = false; 

 

050             delayedEnable = false;  

 

051             callback = new TimerCallback(this.MyTimerCallback); 

 

052         }  

 

053    

 

054         /// <devdoc> 

 

055         ///    <para>  

 

056         ///       Initializes a new instance of the <see cref='System.Timers.Timer'/> class, setting the <see cref='System.Timers.Timer.Interval'/> property to the specified period. 

 

057         ///    </para> 

 

058         /// </devdoc> 

 

059         public Timer(double interval)  

 

060         : this() { 

 

061             if (interval <= 0)  

 

062                 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "interval", interval));  

 

063   

 

064             int i = (int)Math.Ceiling(interval);  

 

065             if( i < 0) { 

 

066                 throw new ArgumentException(SR.GetString(SR.InvalidParameter, "interval", interval)); 

 

067             } 

 

068    

 

069             this.interval = interval; 

 

070         }  

 

071    

 

072         /// <devdoc> 

 

073         /// <para>Gets or sets a value indicating whether the Timer raises the Tick event each time the specified  

 

074         /// Interval has elapsed, 

 

075         ///    when Enabled is set to true.</para> 

 

076         /// </devdoc> 

 

077         [Category("Behavior"),  TimersDescription(SR.TimerAutoReset), DefaultValue(true)]  

 

078         public bool AutoReset { 

 

079             get {  

 

080                 return this.autoReset;   

 

081             } 

 

082    

 

083             set { 

 

084                 if (DesignMode) 

 

085                      this.autoReset = value; 

 

086                 else if (this.autoReset != value) {  

 

087                      this.autoReset = value; 

 

088                     if( timer != null) {  

 

089                          UpdateTimer();  

 

090                     } 

 

091                 }  

 

092             } 

 

093         } 

 

094   

 

095         /// <devdoc>  

 

096         /// <para>Gets or sets a value indicating whether the <see cref='System.Timers.Timer'/> 

 

097         /// is able  

 

098         /// to raise events at a defined interval.</para>  

 

099         /// </devdoc> 

 

100         //[....] - The default value by design is false, don't change it.  

 

101         [Category("Behavior"), TimersDescription(SR.TimerEnabled), DefaultValue(false)] 

 

102         public bool Enabled { 

 

103             get { 

 

104                 return this.enabled;  

 

105             } 

 

106    

 

107             set {  

 

108                 if (DesignMode) { 

 

109                     this.delayedEnable = value;  

 

110                     this.enabled = value; 

 

111                 } 

 

112                 else if (initializing) 

 

113                     this.delayedEnable = value;  

 

114                 else if (enabled != value) { 

 

115                     if (!value) {  

 

116                         if( timer != null) {  

 

117                             cookie = null; 

 

118                             timer.Dispose();  

 

119                             timer = null; 

 

120                         } 

 

121                         enabled = value; 

 

122                     }  

 

123                     else { 

 

124                         enabled = value;  

 

125                         if( timer == null) {  

 

126                             if (disposed) { 

 

127                                 throw new ObjectDisposedException(GetType().Name);  

 

128                             } 

 

129   

 

130                             int i = (int)Math.Ceiling(interval); 

 

131                             cookie = new Object();  

 

132                             timer = new System.Threading.Timer(callback, cookie, i, autoReset? i:Timeout.Infinite); 

 

133                         }  

 

134                         else {  

 

135                             UpdateTimer(); 

 

136                         }  

 

137                     } 

 

138   

 

139                 } 

 

140           }  

 

141         } 

 

142    

 

143    

 

144         private void UpdateTimer() { 

 

145             int i = (int)Math.Ceiling(interval);  

 

146             timer.Change(i, autoReset? i :Timeout.Infinite ); 

 

147         } 

 

148   

 

149         /// <devdoc>  

 

150         ///    <para>Gets or 

 

151         ///       sets the interval on which  

 

152         ///       to raise events.</para>  

 

153         /// </devdoc> 

 

154         [Category("Behavior"), TimersDescription(SR.TimerInterval), DefaultValue(100d), RecommendedAsConfigurable(true)]  

 

155         public double Interval { 

 

156             get { 

 

157                 return this.interval; 

 

158             }  

 

159   

 

160             set {  

 

161                 if (value <= 0)  

 

162                     throw new ArgumentException(SR.GetString(SR.TimerInvalidInterval, value, 0)); 

 

163    

 

164                 interval = value; 

 

165                 if (timer != null) { 

 

166                     UpdateTimer(); 

 

167                 }  

 

168             } 

 

169         }  

 

170    

 

171   

 

172         /// <devdoc>  

 

173         /// <para>Occurs when the <see cref='System.Timers.Timer.Interval'/> has 

 

174         ///    elapsed.</para> 

 

175         /// </devdoc> 

 

176         [Category("Behavior"), TimersDescription(SR.TimerIntervalElapsed)]  

 

177         public event ElapsedEventHandler Elapsed { 

 

178             add {  

 

179                 onIntervalElapsed += value;  

 

180             } 

 

181             remove {  

 

182                 onIntervalElapsed -= value; 

 

183             } 

 

184         } 

 

185    

 

186         /// <devdoc> 

 

187         ///    <para>  

 

188         ///       Sets the enable property in design mode to true by default.  

 

189         ///    </para> 

 

190         /// </devdoc>  

 

191         /// <internalonly/> 

 

192         public override ISite Site { 

 

193             set { 

 

194                 base.Site = value;  

 

195                 if (this.DesignMode) 

 

196                     this.enabled= true;  

 

197             }  

 

198   

 

199             get {  

 

200                 return base.Site; 

 

201             } 

 

202         } 

 

203    

 

204   

 

205         /// <devdoc>  

 

206         ///    <para>Gets or sets the object used to marshal event-handler calls that are issued when  

 

207         ///       an interval has elapsed.</para> 

 

208         /// </devdoc>  

 

209         [ 

 

210         Browsable(false), 

 

211         DefaultValue(null), 

 

212         TimersDescription(SR.TimerSynchronizingObject)  

 

213         ] 

 

214         public ISynchronizeInvoke SynchronizingObject {  

 

215             get {  

 

216                 if (this.synchronizingObject == null && DesignMode) { 

 

217                     IDesignerHost host = (IDesignerHost)GetService(typeof(IDesignerHost));  

 

218                     if (host != null) { 

 

219                         object baseComponent = host.RootComponent; 

 

220                         if (baseComponent != null && baseComponent is ISynchronizeInvoke) 

 

221                             this.synchronizingObject = (ISynchronizeInvoke)baseComponent;  

 

222                     } 

 

223                 }  

 

224    

 

225                 return this.synchronizingObject; 

 

226             }  

 

227   

 

228             set { 

 

229                 this.synchronizingObject = value; 

 

230             }  

 

231         } 

 

232    

 

233         /// <devdoc>  

 

234         ///    <para> 

 

235         ///       Notifies  

 

236         ///       the object that initialization is beginning and tells it to stand by. 

 

237         ///    </para> 

 

238         /// </devdoc> 

 

239         public void BeginInit() {  

 

240             this.Close(); 

 

241             this.initializing = true;  

 

242         }  

 

243   

 

244         /// <devdoc>  

 

245         ///    <para>Disposes of the resources (other than memory) used by 

 

246         ///       the <see cref='System.Timers.Timer'/>.</para> 

 

247         /// </devdoc> 

 

248         public void Close() {  

 

249             initializing = false; 

 

250             delayedEnable = false;  

 

251             enabled = false;  

 

252   

 

253             if (timer != null ) {  

 

254                 timer.Dispose(); 

 

255                 timer = null; 

 

256             } 

 

257         }  

 

258   

 

259         /// <internalonly/>  

 

260         /// <devdoc>  

 

261         /// </devdoc> 

 

262         protected override void Dispose(bool disposing) {  

 

263             Close(); 

 

264             this.disposed = true; 

 

265             base.Dispose(disposing); 

 

266         }  

 

267   

 

268         /// <devdoc>  

 

269         ///    <para>  

 

270         ///       Notifies the object that initialization is complete. 

 

271         ///    </para>  

 

272         /// </devdoc> 

 

273         public void EndInit() { 

 

274             this.initializing = false; 

 

275             this.Enabled = this.delayedEnable;  

 

276         } 

 

277    

 

278         /// <devdoc>  

 

279         /// <para>Starts the timing by setting <see cref='System.Timers.Timer.Enabled'/> to <see langword='true'/>.</para> 

 

280         /// </devdoc>  

 

281         public void Start() { 

 

282             Enabled = true; 

 

283         } 

 

284    

 

285         /// <devdoc> 

 

286         ///    <para>  

 

287         ///       Stops the timing by setting <see cref='System.Timers.Timer.Enabled'/> to <see langword='false'/>.  

 

288         ///    </para> 

 

289         /// </devdoc>  

 

290         public void Stop() { 

 

291             Enabled = false; 

 

292         } 

 

293    

 

294         private void MyTimerCallback(object state) { 

 

295             // System.Threading.Timer will not cancel the work item queued before the timer is stopped.  

 

296             // We don't want to handle the callback after a timer is stopped.  

 

297             if( state != cookie) { 

 

298                 return;  

 

299             } 

 

300   

 

301             if (!this.autoReset) { 

 

302                 enabled = false;  

 

303             } 

 

304    

 

305             FILE_TIME filetime = new FILE_TIME();  

 

306             GetSystemTimeAsFileTime(ref filetime); 

 

307             ElapsedEventArgs elapsedEventArgs = new ElapsedEventArgs(filetime.ftTimeLow, filetime.ftTimeHigh);  

 

308             try { 

 

309                 // To avoid ---- between remove handler and raising the event 

 

310                 ElapsedEventHandler intervalElapsed = this.onIntervalElapsed; 

 

311                 if (intervalElapsed != null) {  

 

312                     if (this.SynchronizingObject != null && this.SynchronizingObject.InvokeRequired) 

 

313                         this.SynchronizingObject.BeginInvoke(intervalElapsed, new object[]{this, elapsedEventArgs});  

 

314                     else 

 

315                        intervalElapsed(this,  elapsedEventArgs); 

 

316                 }  

 

317             } 

 

318             catch { 

 

319             } 

 

320         }  

 

321   

 

322         [StructLayout(LayoutKind.Sequential)]  

 

323         internal struct FILE_TIME {  

 

324             internal int ftTimeLow; 

 

325             internal int ftTimeHigh;  

 

326         } 

 

327   

 

328         [DllImport(ExternDll.Kernel32), SuppressUnmanagedCodeSecurityAttribute()] 

 

329         internal static extern void GetSystemTimeAsFileTime(ref FILE_TIME lpSystemTimeAsFileTime);        

 

330     } 

 

331 }  

 

332    

 

333   

 

334 // File provided for Reference Use Only by Microsoft Corporation (c) 2007.

 

 

 

在初始化的时候它的代码实现是这样的.

 

1 public Timer()  

 

2 : base() { 

 

3     interval = 100;  

 

4     enabled = false; 

 

5     autoReset = true; 

 

6     initializing = false; 

 

7     delayedEnable = false;  

 

8     callback = new TimerCallback(this.MyTimerCallback); 

 

9 }

 

 

 

 

 

而如果你是这样的话

 

01 public Timer(double interval)  

 

02        : this() { 

 

03            if (interval <= 0)  

 

04                throw new ArgumentException(SR.GetString(SR.InvalidParameter, "interval", interval));  

 

05  

 

06            int i = (int)Math.Ceiling(interval);  

 

07            if( i < 0) { 

 

08                throw new ArgumentException(SR.GetString(SR.InvalidParameter, "interval", interval)); 

 

09            } 

 

10   

 

11            this.interval = interval; 

 

12        }

 

 

 

 

 

你就需要再设置下AutoReset = True;

 

 

 

我们加载事件的Elapsed的代码实现是这样的.

 

01 /// <devdoc>  

 

02 /// <para>Occurs when the <see cref='System.Timers.Timer.Interval'/> has 

 

03 ///    elapsed.</para> 

 

04 /// </devdoc> 

 

05 [Category("Behavior"), TimersDescription(SR.TimerIntervalElapsed)]  

 

06 public event ElapsedEventHandler Elapsed { 

 

07     add {  

 

08         onIntervalElapsed += value;  

 

09     } 

 

10     remove {  

 

11         onIntervalElapsed -= value; 

 

12     } 

 

13 }

 

 

 

 

 

对它的基本原理有一定了解后,我们开始写一个简单的实现程序。

 

01 using System;  

 

02 using System.Collections.Generic; 

 

03 using System.Linq; 

 

04 using System.Text; 

 

05 using System.Threading; 

 

06 using Timer = System.Timers.Timer; 

 

07 using System.Timers; 

 

08   

 

09 namespace TestMultipleThread 

 

10 { 

 

11   

 

12     public class ThreadWork 

 

13     { 

 

14         private System.Timers.Timer _TestTimerEvent; 

 

15   

 

16         public void StartWork() 

 

17         { 

 

18             _TestTimerEvent = new Timer(); 

 

19             _TestTimerEvent.Elapsed += Sum; 

 

20             _TestTimerEvent.Start(); 

 

21         } 

 

22   

 

23         public static object lockobject = new object(); 

 

24   

 

25   

 

26         private void Sum(object sender, ElapsedEventArgs e) 

 

27         { 

 

28             Console.WriteLine(string.Format("this is thread ID {0}  execute", Thread.CurrentThread.ManagedThreadId)); 

 

29             for (int i = 0; i < 10000; i++) 

 

30             { 

 

31                 Thread.Sleep(10); 

 

32             } 

 

33         } 

 

34     } 

 

35   

 

36     class Program 

 

37     { 

 

38         public static void Main() 

 

39         { 

 

40             ThreadWork threadWork = new ThreadWork(); 

 

41             ThreadStart myThreadDelegate = new ThreadStart(threadWork.StartWork); 

 

42             Thread myThread = new Thread(myThreadDelegate); 

 

43             myThread.Start(); 

 

44   

 

45             Thread.Sleep(1000000); 

 

46         } 

 

47     } 

 

48 }

 

 

 

 

 

查看的运行结果是:

 

 

image

 

我们看下执行的线程数有多少

image

能说明的一个问题就是在timer每次执行时都会新起一个线程来执行。

 

 

 

作者:spring yang