timer.cs

来自「没的 没的 没的 没的 没的 没的 没的 没的 没的 没的 没的 没的 没的 没」· CS 代码 · 共 754 行 · 第 1/2 页

CS
754
字号
			/// </remarks>			private class PriorityQueue			{				//				// Priority queue represented as a balanced binary heap: the				// two children of queue[n] are queue[2*n] and queue[2*n+1].				// The priority queue is ordered on the nextExecutionTime				// field: The Alarm with the lowest ExpiryTime is in queue[1]				// (assuming the queue is nonempty).  For each node n in the				// heap, and each descendant of n, d:				//   n.ExpiryTime <= d.ExpiryTime. 				//				private Alarm[] queue = new Alarm[4];				//				// The number of timeouts in the priority queue.  (The Alarms				// are stored in queue[1] up to queue[count]).				//				private int count = 0;				/// <summary>Adds an alarm to the priority queue.</summary>				public void Add(Alarm alarm)				{					//					// Grow backing store if necessary					//					if (++this.count == this.queue.Length)					{						Alarm[] newQueue = new Alarm[2 * this.count];						Array.Copy(this.queue, 1, newQueue, 1, this.count - 1);						this.queue = newQueue;					}					this.queue[this.count] = alarm;					this.fixUp(this.count);				}				/// <summary>Get the alarm next due to fire.</summary>				/// <remarks>				///     Return the "head Alarm" of the priority queue.  This				///     is the Alarm with the lowest ExpiryTime.  <c>null</c>				///     is returned if there is no alarm pending.				/// </remarks>				public Alarm Head				{					get { return this.queue[1]; }				}				/// <summary>Remove an alarm from the priority queue.</summary>				public void Remove(Alarm alarm)				{					int index =						System.Array.IndexOf(this.queue, alarm, 1, this.count);					if (index == -1)		// Alarm not in the queue?						return;					int oldCount = this.count;					this.count -= 1;					if (index != oldCount)	// Not last alarm in the queue?					{						//						// Delete the alarm by overwriting it with the last one						// in the queue, then re-adjusting the heap to maintain						// the invariant.						//						this.queue[index] = this.queue[oldCount];						this.fixDown(index);					}					this.queue[oldCount] = null; // Prevent memory leaks!				}				//				// Establishes the heap invariant (described above) assuming				// the heap satisfies the invariant except possibly for the				// leaf-node indexed by k (which may have a ExpiryTime less				// than its parent's).				//				// This method functions by "promoting" queue[k] up the				// hierarchy (by swapping it with its parent) repeatedly until				// queue[k]'s expireTime is greater than or equal to that of				// its parent.				//				private void fixUp(int k)				{					while (k > 1)					{						int j = k >> 1;						if (this.queue[j].ExpiryTime <= this.queue[k].ExpiryTime)							break;						Alarm tmp = this.queue[j];						this.queue[j] = this.queue[k];						this.queue[k] = tmp;						k = j;					}				}				//				// Establishes the heap invariant (described above) in the				// subtree rooted at k, which is assumed to satisfy the heap				// invariant except possibly for node k itself (which may have				// a ExpiryTime greater than its children's).				// 				// This method functions by "demoting" queue[k] down the				// hierarchy (by swapping it with its smaller child) repeatedly				// until queue[k]'s expireTime is less than or equal to those				// of its children.				//				private void fixDown(int k)				{					int j;					while ((j = k << 1) <= this.count)					{						if (j < this.count && this.queue[j].ExpiryTime > this.queue[j+1].ExpiryTime)							j += 1; // j indexes smallest kid						if (this.queue[k].ExpiryTime <= this.queue[j].ExpiryTime)							break;						Alarm tmp = this.queue[j];						this.queue[j] = this.queue[k];						this.queue[k] = tmp;						k = j;					}				}			}			public interface IAlarm			{				/// <summary>Set the Alarm.</summary>				/// <param name="dueTime">The amount of time to delay before				///     firing the event for the first time.  If this is 0, the				///     event is fired immediately.  If this is				///     <see cref="INFINITE"/> the Alarm is disabled, meaning				///     it never fires.</param>				/// <param name="period">After the event is fired, the time to				///     delay before firing it again.  If this is				///     <see cref="INFINITE"/> is will never be fired				///     again.</param>				///				/// <remarks>				///     The event to fire is passed to				///     <see cref="CreateAlarm"/>.  The units used to measure				///     time is up to you.  Just be sure they are consistent				///     with what is passed to <see cref="Sleep"/>.				/// </remarks>				void SetAlarm(long dueTime, long period);			}			/// <summary>Fired when an alarm expires.</summary>			public delegate void AlarmExpiredHandler();			/// <summary>Implementation of an IAlarm.</summary>			/// <remarks>			///     No one is meant to look inside of this - except the Alarm			///     Clock.			/// </remarks>			private class Alarm : IAlarm			{				public long					ExpiryTime;  // DISABLED or when next due				public readonly AlarmExpiredHandler	AlarmExpired;				private readonly AlarmClock	alarmClock;	// Our owner				private long				period;		// Periodic interval				private const long			DISABLED = -1;				/// <summary>Create a new Alarm.</summary>				public Alarm(AlarmClock alarmClock, AlarmExpiredHandler alarmExpired)				{					this.alarmClock = alarmClock;					this.AlarmExpired = alarmExpired;					this.ExpiryTime = DISABLED;				}				public void SetAlarm(long dueTime, long period)				{					//					// Check arguments.					//					if (dueTime < 0)					{						throw new ArgumentOutOfRangeException(							"dueTime", _("ArgRange_NonNegative"));					}					if (period <= 0)					{						throw new ArgumentOutOfRangeException(							"period", _("ArgRange_PositiveNonZero"));					}					//					// Lock the clock while the alarm is scheduled for the					// first time.					//					lock (this)					{						//						// First a quick check to see if anything has changed.						// This has to be done, because this routine may be						// called by a finalizer wishing to clean up.  The						// the finalizer will of been called by the garbage						// collector because it was asked for more memory,						// and there isn't any.  The thread asking for memory						// may be in here, allocating a new Alarm, say.						// Because it is in here, the AlarmClock object may						// be locked.  If we should then try to lock the						// AlarmClock, the finializer will block, which means						// the memory won't be freed, which means the other						// thread will remain blocked, which means we managed						// to dead lock the garbage collector.  Oh joy.  A						// new reason to hate threads.  And yes, this really						// did happen during testing.						//						// The key to this mess is to realise that if this						// object is being garbage collected, there must be						// no references to it.  In particular, it is not in						// the alarm queue, so its ExpiryTime must be DISABLED.						// Also, since this object is being garbage collected,						// the only reasonable thing the finalizer can be doing						// in here it ensuring this alarm is disabled.						//						// So, if this call is disabling an already disabled						// alarm, then under no circumstances must we try to						// acquire a lock on AlarmClock, as doing so may						// deadlock the garbage collector.						//						if (dueTime == INFINITE && this.ExpiryTime == DISABLED)							return;					}					//					// The locks must be released and re-acquired in the same					// order as Sleep/Advance does it below, or again we risk					// deadlock.					//					lock (this.alarmClock)					{						lock (this)						{							if (this.ExpiryTime != DISABLED)								this.alarmClock.pending.Remove(this);							if (dueTime == INFINITE)							{								this.ExpiryTime = DISABLED;								return;							}							this.period = period;							this.ExpiryTime = this.alarmClock.now + dueTime;							this.alarmClock.pending.Add(this);						}					}				}				/// <summary>Advance the time for this alarm.</summary>				/// <param name="to">The proposed new value for the				///     time.</param>				/// <returns>The time this alarm is due to go off.</returns>				///				/// <remarks>				///     Must be called while the lock on the clock is held.				///     If the Alarm would expire in the interval passed it				///     is rescheduled or disabled, depending on its settings.				/// </remarks>				public long Advance(long to)				{					long oldExpiryTime;					lock (this)					{						oldExpiryTime = this.ExpiryTime;						if (this.ExpiryTime <= to)						{							this.alarmClock.pending.Remove(this);							if (this.period == INFINITE)								this.ExpiryTime = DISABLED;							else							{								this.ExpiryTime += this.period;								this.alarmClock.pending.Add(this);							}						}					}					return oldExpiryTime;				}			};			//			// The current time.			//			private long			now = 0;			//			// A list of all enabled Alarm's.			//			private PriorityQueue	pending = new PriorityQueue();			/// <summary>Create a new IAlarm.</summary>			/// <remarks>			///     The IAlarm is initially disabled.			/// </remarks>			public IAlarm CreateAlarm(AlarmExpiredHandler alarmExpired)			{				if (alarmExpired == null)					throw new ArgumentNullException("alarmExpired");				return new Alarm(this, alarmExpired);			}			/// <summary>Advance the time.</summary>			/// <param name="period">The amount to advance the time by.</param>			///			/// <remarks>			///     Advance the time, and fire all <see cref="IAlarm"/>'s that			///     exipred during that period.  Because this method may fire			///     events it must be called *OUTSIDE OF ALL LOCKS*.  The units			///     used to measure time are up to you - just be sure to keep			///     them consistent with <see cref="Alarm.SetAlarm"/>.  			/// </remarks>			public void Sleep(long period)			{				for (;;)				{					Alarm nextAlarm;					lock (this)					{						//						// Find the event to occur.						//						long endSleep = this.now + period;						nextAlarm = this.pending.Head;						//						// If there another alarm in the queue?						//						if (nextAlarm == null)						{							this.now = endSleep;							return;						}						//						// Is the alarm due to go off?						//						long nextExpiryTime = nextAlarm.Advance(endSleep);						if (nextExpiryTime > endSleep)						{							this.now = endSleep;							return;						}						//						// An alarm has expired.  Advance the time to equal						// its current expiry time.						//						period -= nextExpiryTime - this.now;						this.now = nextExpiryTime;					}					//					// Must be called outside of the lock to avoid potential					// deadlocks.					//					nextAlarm.AlarmExpired();				}			}			/// <summary>The delay until the next alarm will fire.</summary>			/// <returns>			///     The amount of time until the next <see cref="IAlarm"/>			///     will fire, or <see cref="INFINITE"/> if there is no			///     <c>Alarm</c>'s pending.  The units are the same as for the			///     period passed to <see cref="Sleep"/>.			/// </returns>			public long TimeTillAlarm			{				get				{					lock (this)					{						Alarm nextAlarm = this.pending.Head;						if (nextAlarm == null)							return INFINITE;						return nextAlarm.ExpiryTime - this.now;					}				}			}		}	}; // class Timer}; // namespace System.Threading

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?