/// <summary> /// Creates a new <see cref="Delegator"/> instance which invokes the /// given delegates when <see cref="SetUp"/> and <see cref="TearDown"/> /// are called. /// </summary> /// <param name="setup"></param> /// The delegate to invoke when <see cref="SetUp"/> is called. /// <param name="teardown"> /// The delegate to invoke when <see cref="TearDown"/> is called. /// </param> public Delegator(EventQueue dispatcher, Collection <V> collection, SetUpDelegate setup, TearDownDelegate teardown) : base(dispatcher, collection) { this.s = setup; this.t = (teardown != null) ? teardown : new TearDownDelegate(base.TearDown); }
/// <summary> /// This creates a new timer that runs on a new thread (not threadpool thread). All three of the delegates passed in will be executed /// from that newly created threads /// </summary> /// <param name="prep"> /// This gives the caller a chance to set up objects for the tick to use (those objects won't need to be threadsafe, because they will be created and /// used only on the background thread for this instance of timer /// </param> /// <param name="tick"> /// This gets fired every internal (arg is the object returned from prep /// NOTE: If the code in this tick takes longer than interval, subsequent ticks will just get eaten, so no danger /// </param> /// <param name="tearDown"> /// This gets called when this timer is disposed. Gives the user a chance to dispose anything they created in prep /// </param> public TimerCreateThread(PrepDelegate prep = null, TickDelegate tick = null, TearDownDelegate tearDown = null, params object[] prepArgs) { //TODO: figure out how to use ParameterizedThreadStart instead of using member variables _prep = prep; _prepArgs = prepArgs; _tick = tick; _tearDown = tearDown; Thread workerThread = new Thread(WorkerMethod); workerThread.IsBackground = true; workerThread.Start(); }
private void WorkerMethod() { // Doing this so TaskScheduler.FromCurrentSynchronizationContext() will work SynchronizationContext.SetSynchronizationContext(new SynchronizationContext()); // Since _tearDown gets called after dispose, there's a slight chance that the underlying object could get garbage collected, so store local copies of these // two delegates up front TickDelegate tick = _tick; TearDownDelegate tearDown = _tearDown; // Let the client create an object from this thread T prepResults = default(T); if (_prep != null) { prepResults = _prep(_prepArgs); } #region Set up the timer DateTime lastTick = DateTime.UtcNow; ManualResetEvent tickEvent = new ManualResetEvent(false); System.Timers.Timer timer = new System.Timers.Timer(); //timer.SynchronizingObject //NOTE: I tried to implement ISynchonizeInvoke, but that approach was a mess. I think this approach of creating a thead for the user is way cleaner timer.Interval = this.Interval; timer.Elapsed += delegate { tickEvent.Set(); }; timer.AutoReset = false; if (_isTimerRunning) { timer.Start(); } #endregion WaitHandle[] handles = new WaitHandle[] { _disposeEvent, _timerSettingsChanged, tickEvent }; while (true) { // Hang this thread until something happens int handleIndex = WaitHandle.WaitAny(handles); if (handleIndex == 0) // I would prefer a switch statement, but there is no Exit While statement { // Dispose was called, this thread needs to finish //_disposeEvent.Reset(); // just leave this one tripped so that break; } else if (handleIndex == 1) { #region Timer settings changed _timerSettingsChanged.Reset(); // Settings for the timer have changed timer.Interval = this.Interval; if (_isTimerRunning) { timer.Start(); // it appears to be safe to call this even when started } else { timer.Stop(); // and to call this again when stopped } #endregion } else if (handleIndex == 2) { #region Timer ticked DateTime currentTick = DateTime.UtcNow; double elapsed = (currentTick - lastTick).TotalMilliseconds; lastTick = currentTick; tickEvent.Reset(); if (_isTimerRunning) { timer.Start(); if (tick != null) // putting this inside the _isTimerRunning, because they may have wanted the timer stopped between ticks, so in that case, just ignore this tick event { tick(prepResults, elapsed); } } #endregion } else { throw new ApplicationException("Unknown wait handle: " + handleIndex.ToString()); } } timer.Stop(); timer.Dispose(); if (tearDown != null) { tearDown(prepResults); } }