Is this background thread queue a realistic implementation?

In particular, I am wondering:

Will a ManualResetEvent resource consume resources while it is idle? Is context switching performance degradation related to idle threads?

If I have the choice to use multiple BackgroundThreadQueues, which do less work each or one BackgroundThreadQueue, which works more, and I choose to use several ... will the queues of waiting threads affect the performance of the process while they do nothing?

Is there a better FIFO thread queue I should use in C # or some other blocking strategy?

Any suggestions are welcome.

/// <summary> /// This class is responsible for peforming actions in a FIFO order on a /// background thread. When it is constructed, a background thread is created /// and a manual reset event is used to trigger actions to be performed when /// a new action is enqueued, or when one finishes. There is a ShuttingDown /// flag that is set by calling code when it is time to destroy the thread, /// and a QueueIsEmpty event is fired whenever the queue finishes executing /// the last action. /// </summary> public class BackgroundThreadQueue : IBackgroundThreadQueue { #region Fields private readonly Queue<Action> queueOfActions = new Queue<Action>(); readonly ManualResetEvent resetEvent; private bool shuttingDown; private bool readyToShutdown; private readonly object lockObject = new object(); private string queuName; #endregion Fields #region Events /// <summary> /// Occurs when the BackgroundThreadQueue is empty, and ready to shut down. /// </summary> public event EventHandler IsReadyToShutdown; #endregion Events #region Constructor public BackgroundThreadQueue(string threadName) { this.resetEvent = new ManualResetEvent(false); queuName = threadName; StartThread(); } #endregion Constructor #region Public Methods public void ClearQueue() { lock (lockObject) { queueOfActions.Clear(); } resetEvent.Set(); } /// <summary> /// Enqueues an action, and calls set on the manual reset event to trigger /// the action to be performed (if no action is currently being performed, /// the one just enqueued will be done immediately, if an action is already /// being performed, then the one just enqueued will have to wait its turn). /// </summary> public void EnqueueAction(Action actionToEnqueue) { if (actionToEnqueue == null) { throw new ArgumentNullException("actionToEnqueue"); } bool localReadyToShutDown = false; lock (lockObject) { queueOfActions.Enqueue(actionToEnqueue); if(this.readyToShutdown) { localReadyToShutDown = true; this.readyToShutdown = false; } } //if this instance is ready to shut down...and we just enqueued a //new action...we can't shut down now... if (localReadyToShutDown) { StartThread(); } resetEvent.Set(); } #endregion Public Methods #region Public Properties public bool ReadyToShutdown { get { lock (lockObject) { return this.shuttingDown && this.readyToShutdown; } } private set { this.readyToShutdown = value; if (this.readyToShutdown) { //let interested parties know that the queue is now empty //and ready to shutdown IsReadyToShutdown.Raise(this); } } } /// <summary> /// Gets or sets a value indicating whether or not the queue should shut down /// when it is finished with the last action it has enqueued to process. /// If the queues owner is shutting down, it needs to notify the queue, /// and wait for a QueueIsEmpty event to be fired, at which point the reset /// event will exit ... the owner shouldn't actually destroy the queue /// until all actions have been performed. /// </summary> public bool ShuttingDown { get { lock (lockObject) { return this.shuttingDown; } } set { lock (lockObject) { bool startThread = false; if (value == false) { readyToShutdown = false; //if we were shutting down...but, now are not startThread = this.shuttingDown; } this.shuttingDown = value; //if we were shutting down, but now are not... //we need to restart the processing actions thread if (startThread) { StartThread(); } } this.resetEvent.Set(); } } #endregion Public Properties #region Private Methods private void StartThread() { var processActionsThread = new Thread(this.ProcessActions); processActionsThread.Name = queuName; processActionsThread.IsBackground = true; processActionsThread.Start(); } /// <summary> /// Processes the actions in a while loop, resetting a ManualResetEvent that /// is triggered in the EnqueueAction method and ShuttingDown property. /// </summary> private void ProcessActions() { while (true) { Action action = null; lock (lockObject) { //if there are any actions, then get the first one out of the queue if (queueOfActions.Count > 0) { action = queueOfActions.Dequeue(); } } if (action != null) { action(); } lock (lockObject) { //if any actions were added since the last one was processed, go //back around the loop and do the next one if (this.queueOfActions.Count > 0) { continue; } if (this.shuttingDown) { //ReadyToShutdown setter will raise IsReadyToShutdown ReadyToShutdown = true; //get out of the method if the user has chosen to shutdown, //and there are no more actions to process return; } this.resetEvent.Reset(); } this.resetEvent.WaitOne(); } } #endregion Private Methods } 
0
source share
2 answers

The mere existence of pending threads should not have a permanent effect on performance outside of the memory reserved for the actual thread stack and any credentials that the runtime or kernel continues to consider. So no, really pending threads are not going to do anything but use RAM.

However, I'm not sure why you will write this code, since .Net has built-in threadpool and concurrency tools, which you will probably prefer on your own.

0
source

Threads blocked by a call to ManualResetEvent.WaitOne() are removed from the CPU and are not considered again for OS planning until an event occurs that should awaken them (i.e., a call to Set() ). Therefore, expecting that they will be signaled, they are inactive and do not consume processor cycles.

+1
source

Source: https://habr.com/ru/post/1397223/


All Articles