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

时间:2021-11-11 07:40: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 }

查看的运行结果是:

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

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

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

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