441 lines · 14.9 KB · C#
RawDownload
//******************************************************************************************************//  LogicalThread.cs - Gbtc////  Copyright © 2015, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  09/24/2015 - Stephen C. Wills//       Generated original version of source code.//  02/16/2016 - J. Ritchie Carroll//       Changed default priority level to 1 - normal / lowest.////****************************************************************************************************** using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.Linq;using System.Threading; namespace GSF.Threading{    /// <summary>    /// Represents a set of statistics gathered about    /// the execution time of actions on a logical thread.    /// </summary>    public class LogicalThreadStatistics    {        #region [ Members ]         // Fields        private TimeSpan m_maxExecutionTime;        private TimeSpan m_minExecutionTime;        private TimeSpan m_totalExecutionTime;        private long m_executionCount;         #endregion         #region [ Properties ]         /// <summary>        /// Gets the execution time of the longest running action.        /// </summary>        public TimeSpan MaxExecutionTime        {            get            {                return m_maxExecutionTime;            }        }         /// <summary>        /// Gets the execution time of the shortest running action.        /// </summary>        public TimeSpan MinExecutionTime        {            get            {                return m_minExecutionTime;            }        }         /// <summary>        /// Gets the total execution time of all actions executed on the logical thread.        /// </summary>        public TimeSpan TotalExecutionTime        {            get            {                return m_totalExecutionTime;            }        }         /// <summary>        /// Gets the total number of actions executed on the logical thread.        /// </summary>        public long ExecutionCount        {            get            {                return m_executionCount;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Updates the statistics based on the execution time of a single action.        /// </summary>        /// <param name="executionTime">The execution time of the action.</param>        internal void UpdateStatistics(TimeSpan executionTime)        {            if (m_executionCount == 0 || executionTime > m_maxExecutionTime)                m_maxExecutionTime = executionTime;             if (m_executionCount == 0 || executionTime < m_minExecutionTime)                m_minExecutionTime = executionTime;             m_totalExecutionTime += executionTime;            m_executionCount++;        }         #endregion    }     /// <summary>    /// Represents a thread of execution to which    /// actions can be dispatched from other threads.    /// </summary>    /// <remarks>    /// <para>    /// This class provides a simple alternative to synchronization primitives    /// such as wait handles and locks. Actions dispatched to a logical thread    /// will be processed synchronously as though it was executed as consecutive    /// method calls. All such actions can be dispatched from any thread in the    /// system so method calls coming from multiple threads can be easily    /// synchronized without locks, loops, wait handles, or timeouts.    /// </para>    ///     /// <para>    /// Note that the <see cref="LogicalThreadScheduler"/> implements its own    /// thread pool to execute tasks pushed to logical threads. Executing    /// long-running processes or using synchronization primitives with high    /// contention or long timeouts can hinder the logical thread scheduler's    /// ability to schedule the actions of other logical threads. Like other    /// thread pool implementations, you can mitigate this by increasing the    /// maximum thread count of the logical thread scheduler, however it is    /// recommended to avoid using synchronization primitives and instead    /// synchronize those operations by running them as separate actions on    /// the same logical thread.    /// </para>    /// </remarks>    public class LogicalThread    {        #region [ Members ]         // Events         /// <summary>        /// Handler for unhandled exceptions on the thread.        /// </summary>        public event EventHandler<EventArgs<Exception>> UnhandledException;         // Fields        private LogicalThreadScheduler m_scheduler;        private ConcurrentQueue<Action>[] m_queues;        private Dictionary<object, object> m_threadLocalStorage;        private ICancellationToken m_nextExecutionToken;        private int m_activePriority;         private LogicalThreadStatistics m_statistics;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the <see cref="LogicalThread"/> class.        /// </summary>        public LogicalThread()            : this(DefaultScheduler)        {        }         /// <summary>        /// Creates a new instance of the <see cref="LogicalThread"/> class.        /// </summary>        /// <param name="scheduler">The <see cref="LogicalThreadScheduler"/> that created this thread.</param>        /// <exception cref="ArgumentNullException"><paramref name="scheduler"/> is null.</exception>        internal LogicalThread(LogicalThreadScheduler scheduler)        {            if ((object)scheduler == null)                throw new ArgumentNullException(nameof(scheduler));             m_scheduler = scheduler;            m_queues = new ConcurrentQueue<Action>[PriorityLevels];            m_threadLocalStorage = new Dictionary<object, object>();            m_nextExecutionToken = new CancellationToken();            m_statistics = new LogicalThreadStatistics();             for (int i = 0; i < m_queues.Length; i++)                m_queues[i] = new ConcurrentQueue<Action>();        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets the number of levels of priority        /// supported by this logical thread.        /// </summary>        public int PriorityLevels        {            get            {                return m_scheduler.PriorityLevels;            }        }         /// <summary>        /// Gets a flag that indicates whether the logical        /// thread has any unprocessed actions left in its queue.        /// </summary>        public bool HasAction        {            get            {                return m_queues.Any(queue => !queue.IsEmpty);            }        }         /// <summary>        /// Gets or sets the priority at which the        /// logical thread is queued by the scheduler.        /// </summary>        internal int ActivePriority        {            get            {                return Interlocked.CompareExchange(ref m_activePriority, 0, 0);            }            set            {                Interlocked.Exchange(ref m_activePriority, value);            }        }         /// <summary>        /// Gets the priority of the next action to be processed on this logical thread.        /// </summary>        internal int NextPriority        {            get            {                return PriorityLevels - m_queues.TakeWhile(queue => queue.IsEmpty).Count();            }        }         /// <summary>        /// Gets or sets the cancellation token for the next time        /// the thread's actions will be executed by the scheduler.        /// </summary>        internal ICancellationToken NextExecutionToken        {            get            {                return Interlocked.CompareExchange(ref m_nextExecutionToken, null, null);            }            set            {                Interlocked.Exchange(ref m_nextExecutionToken, value);            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Pushes an action to the logical thread.        /// </summary>        /// <param name="action">The action to be executed on this thread.</param>        public void Push(Action action)        {            Push(1, action);        }         /// <summary>        /// Pushes an action to the logical thread.        /// </summary>        /// <param name="priority">The priority the action should be given when executing actions on the thread (higher numbers have higher priority).</param>        /// <param name="action">The action to be executed on this thread.</param>        /// <exception cref="ArgumentException"><paramref name="priority"/> is outside the range between 1 and <see cref="PriorityLevels"/>.</exception>        public void Push(int priority, Action action)        {            if (priority < 1 || priority > PriorityLevels)                throw new ArgumentException($"Priority {priority} is outside the range between 1 and {PriorityLevels}.", nameof(priority));             m_queues[PriorityLevels - priority].Enqueue(action);            m_scheduler.SignalItemHandler(this, priority);        }         /// <summary>        /// Clears all actions from the logical thread.        /// </summary>        public void Clear()        {            Action action;             foreach (ConcurrentQueue<Action> queue in m_queues)            {                while (!queue.IsEmpty)                    queue.TryDequeue(out action);            }        }         /// <summary>        /// Samples the statistics, providing current statistic        /// values and resetting statistic counters.        /// </summary>        /// <returns>The current statistic values.</returns>        public LogicalThreadStatistics SampleStatistics()        {            return Interlocked.Exchange(ref m_statistics, new LogicalThreadStatistics());        }         /// <summary>        /// Pulls an action from the logical thread's internal queue to be executed on a physical thread.        /// </summary>        /// <returns>An action from the logical thread's internal queue.</returns>        internal Action Pull()        {            Action action;             foreach (ConcurrentQueue<Action> queue in m_queues)            {                if (queue.TryDequeue(out action))                    return action;            }             return null;        }         /// <summary>        /// Attempts to activate the thread at the given priority.        /// </summary>        /// <param name="priority">The priority at which to activate the thread.</param>        /// <returns>True if the thread's priority needs to be changed to the given priority; false otherwise.</returns>        internal bool TryActivate(int priority)        {            // Always get the execution token before the            // active priority to mitigate race conditions            ICancellationToken nextExecutionToken = NextExecutionToken;            int activePriority = ActivePriority;            return (activePriority < priority) && nextExecutionToken.Cancel();        }         /// <summary>        /// Completely deactivates the thread, making it available for reactivation at any priority.        /// </summary>        internal void Deactivate()        {            // Always update the active priority before the            // execution token to mitigate race conditions            ActivePriority = 0;            NextExecutionToken = new CancellationToken();        }         /// <summary>        /// Returns the thread local object with the given ID.        /// </summary>        /// <param name="key">The key used to look up the thread local object.</param>        /// <returns>The value of the thread local object with the given ID.</returns>        internal object GetThreadLocal(object key)        {            object value;            m_threadLocalStorage.TryGetValue(key, out value);            return value;        }         /// <summary>        /// Sets the value of the thread local object with the given ID.        /// </summary>        /// <param name="key">The key used to look up the thread local object.</param>        /// <param name="value">The new value for the thread local object.</param>        internal void SetThreadLocal(object key, object value)        {            if (value != null)                m_threadLocalStorage[key] = value;            else                m_threadLocalStorage.Remove(key);        }         /// <summary>        /// Updates the statistics based on the execution time of a single action.        /// </summary>        /// <param name="executionTime">The execution time of the action.</param>        internal void UpdateStatistics(TimeSpan executionTime)        {            m_statistics.UpdateStatistics(executionTime);        }         /// <summary>        /// Raises the <see cref="UnhandledException"/> event.        /// </summary>        /// <param name="ex">The unhandled exception.</param>        /// <returns>True if there are any handlers attached to this event; false otherwise.</returns>        internal bool OnUnhandledException(Exception ex)        {            EventHandler<EventArgs<Exception>> unhandledException = UnhandledException;             if ((object)unhandledException == null)                return false;             unhandledException(this, new EventArgs<Exception>(ex));             return true;        }         #endregion         #region [ Static ]         // Static Fields        private static readonly LogicalThreadScheduler DefaultScheduler = new LogicalThreadScheduler();        private static readonly ThreadLocal<LogicalThread> LocalThread = new ThreadLocal<LogicalThread>();         // Static Properties         /// <summary>        /// Gets the logical thread that is currently executing.        /// </summary>        public static LogicalThread CurrentThread        {            get            {                return LocalThread.Value;            }            internal set            {                LocalThread.Value = value;            }        }         #endregion    }}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t