void QueueAction(T next) { var ct = CancelTokenSrc.Token; Task task = new Task(() => { // If we are cancelled before we start, push the job back on the queue so it can be dealt with by another runner. if (ct.IsCancellationRequested) { Queue.Enqueue(next); return; } SafeAction(next); }, ct); task.ContinueWith((t) => { byte val; RunningTasks.TryRemove(t, out val); WaitForTaskEvent.Set( ); }, ct); RunningTasks.TryAdd(task, 0); task.Start( ); }
/// <summary> /// Stop the Actioner /// </summary> /// <param name="timeoutMs">How long to wait in ms for the actioner to complete, -1 to wait forever</param> /// <returns>true if thread was or is stopped.</returns> public bool Stop(int timeoutMs = -1) { if (State != ActionerState.Stopped) { lock (_syncRoot) { if (State == ActionerState.Running || State == ActionerState.WaitingForQueue || State == ActionerState.WaitingForTask) { State = ActionerState.Stopping; } } // cancel running tasks CancelTokenSrc?.Cancel(); } // Get rid of the looping thread if (LoopingThread != null) { WaitForQueueEvent.Set(); WaitForTaskEvent.Set(); if (LoopingThread.Join(timeoutMs)) { LoopingThread = null; return(true); } return(false); } return(true); }
/// <summary> /// The loop that pulls elements from the Queue and acts upon them. /// </summary> private void ActionLoop() { var lastReport = DateTime.UtcNow; while (true) { if (DateTime.UtcNow - lastReport > TimeSpan.FromMilliseconds(TaskReportingTime)) { lastReport = DateTime.UtcNow; Report(); } try { switch (State) { case ActionerState.Running: if (_semaphore.WaitOne(0)) { T next = Queue.Dequeue(); if (next != null) { QueueAction(next); } else { SetStateIfNotStopping(ActionerState.WaitingForQueue); } _semaphore.Release( ); } else { SetStateIfNotStopping(ActionerState.WaitingForTask); } break; case ActionerState.WaitingForQueue: WaitForQueueEvent.WaitOne(TaskReportingTime); SetStateIfNotStopping(ActionerState.Running); break; case ActionerState.WaitingForTask: WaitForTaskEvent.WaitOne(TaskReportingTime); SetStateIfNotStopping(ActionerState.Running); break; case ActionerState.Stopping: if (!Task.WaitAll(RunningTasks.Keys.ToArray(), TaskStopWait)) { EventLog.Application.WriteError("Failed to stop all the running tasks when shutting down QueueActioner."); } State = ActionerState.Stopped; Report(); return; case ActionerState.Stopped: EventLog.Application.WriteError("A Stopped thread should not be processing."); return; } } catch (Exception ex) { EventLog.Application.WriteError($"Unhandled exception in QueueAction loop. Exception: {ex}"); // exception not rethrown } } }