

using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace CsvCount_ES
{
    /// <summary>
    ///这个类实现了一个线程池。缓冲所有工作项。程序缓冲区只是在关闭时清理它们。
    /// </summary>
    public partial class ThreadPool
    {
        /// <summary>
        ///  工作项有效负载的回调委托的形式。 
        /// </summary>
        internal delegate object WorkItemCallback(object state);

        /// <summary>
        ///   当工作项完成时，应该调用的回调函数。
        /// </summary>
        public delegate void CallbackFunction(object o);

        private bool isDisposeDoneWorkItemsAutomatically;
        private readonly Queue<SingleThreadRunner> threads;
        private readonly ConcurrentQueue<SingleThreadRunner> threadsIdle;
        private int threadsWorking;
        private readonly ConcurrentQueue<WorkItem> workItemQueue = new ConcurrentQueue<WorkItem>();

        private readonly ConcurrentQueue<WorkItem> returnedWorkItems = new ConcurrentQueue<WorkItem>();
        private bool shutDownSignaled;
        private readonly object lockObjectShutDownSignaled = new object();

        public int NumberOfThreads { get; }

        /// <summary>
        ///获取或设置一个值，该值指示该实例是否将自动处理完成的工作项。
        ///注意:如果启用此选项，则在完成后立即调用每个工作项的dispose方法，从而销毁引用。
        ///当使用action - interface(不返回值)和<c> WaitForEveryWorkerIdle- method时，这显然只是一个可行的选项。
        /// </summary>
        /// <value>
        ///     <c>true</c> 如果这个实例是自动处理的工作项;否则, <c>false</c>.
        /// </value>
        public bool IsDisposeDoneWorkItemsAutomatically
        {
            get
            {
                Thread.MemoryBarrier();
                return isDisposeDoneWorkItemsAutomatically;
            }
            set
            {
                isDisposeDoneWorkItemsAutomatically = value;
                Thread.MemoryBarrier();
            }
        }

        /// <summary>
        /// 初始化线程池，
        /// </summary>
        /// <param name="numberOfThreads">指定线程数量</param>
        /// <param name="threadsNamePrefix">线程名称前缀</param>
        public ThreadPool(int numberOfThreads, string threadsNamePrefix)
        {
            NumberOfThreads = numberOfThreads;
            threads = new Queue<SingleThreadRunner>();
            threadsIdle = new ConcurrentQueue<SingleThreadRunner>();//闲置

            // 分配线程
            for (var i = 0; i < NumberOfThreads; i++)
            {
                //创建一个“线程框架”执行者
                var singleThreadRunner = new SingleThreadRunner(this);
                singleThreadRunner.Thread = new Thread(singleThreadRunner.DoWork);
                singleThreadRunner.Thread.Name = threadsNamePrefix + (i + 1);//线程名
                singleThreadRunner.Thread.IsBackground = true;
                //入线程队列
                threads.Enqueue(singleThreadRunner);
                //入线程闲置队列
                threadsIdle.Enqueue(singleThreadRunner);

                singleThreadRunner.Thread.Start();
            }
        }

        /// <summary>
        ///    清空工作项队列
        /// </summary>
        public void ClearWorkItemQueue()
        {
            WorkItem wi;
            while (workItemQueue.TryDequeue(out wi))
            {}
        }

        /// <summary>
        ///    仍需要执行的工作项数量
        /// </summary>
        /// <returns></returns>
        public int NumberOfItemsLeft()
        {
            Thread.MemoryBarrier();
            return workItemQueue.Count;
        }

        /// <summary>
        /// 完成处理并返回的工作项数量。
        /// </summary>
        /// <returns></returns>
        public int NumberOfItemsDone()
        {
            Thread.MemoryBarrier();
            return returnedWorkItems.Count;
        }

        /// <summary>
        ///    工作项入队列
        /// </summary>
        /// <param name="workItem">工作项.</param>
        internal void EnqueueWorkItemInternal(WorkItem workItem)
        {
            // 找一个闲置的工作项
            SingleThreadRunner singleThreadRunner;
            if (threadsIdle.TryDequeue(out singleThreadRunner))
            {
                // 移交这个工作项
                workItem.SingleThreadRunner = singleThreadRunner;
                Interlocked.Increment(ref threadsWorking);
                singleThreadRunner.SignalWork(workItem);
            }
            else
            {
                // 全忙、入队
                workItemQueue.Enqueue(workItem);
            }
        }

        /// <summary>
        ///    工作项出队
        /// </summary>
        /// <param name="singleThreadRunner">The single thread runner.</param>
        /// <param name="isGetNewOne">
        ///     if set to <c>true</c> [is get new one].
        /// </param>
        /// <param name="returnedWorkItem">The returned work item.</param>
        /// <returns>
        ///     <see langword="true" />, if a work item has been
        ///     successfully dequeued. <see langword="false" /> otherwise.
        /// </returns>
        internal WorkItem DequeueWorkItemInternal(SingleThreadRunner singleThreadRunner, bool isGetNewOne,
            WorkItem returnedWorkItem = null)
        {
            if (returnedWorkItem != null)
            {
                returnedWorkItems.Enqueue(returnedWorkItem);
            }

            if (!shutDownSignaled && isGetNewOne)
            {
                WorkItem workItem;
                if (workItemQueue.TryDequeue(out workItem))
                {
                    workItem.SingleThreadRunner = singleThreadRunner;
                    return workItem;
                }
            }

            //工作项完成、闲置
            Interlocked.Decrement(ref threadsWorking);
            threadsIdle.Enqueue(singleThreadRunner);
            return null;
        }

        private WorkItem GetWorkItem(CallbackFunction asyncCallback)
        {
            WorkItem workItem;
            if (!returnedWorkItems.TryDequeue(out workItem))
            {
                workItem = new WorkItem();
                workItem.WorkItemStateTypeless = new WorkItemStateTypeless(workItem);
            }

            workItem.SingleThreadRunner = null;
            workItem.IsCompleted = false;
            workItem.Result = null;
            workItem.AsyncCallback = asyncCallback;
            return workItem;
        }

        /// <summary>
        ///     返回工作项
        /// </summary>
        /// <param name="returnedWorkItem">The returned work item.</param>
        public void ReturnWorkItem(WorkItem returnedWorkItem)
        {
            returnedWorkItems.Enqueue(returnedWorkItem);
        }

        /// <summary>
        ///    等待队列空--一般用法，是顺序执行此位置，来确保一个线程池执行完毕。如果是一直用的线程池或异步，则不需要执行此方法。
        /// </summary>
        public void WaitForEveryWorkerIdle()
        {
            //内旋，迫使CPU执行上下文切换,得到线程切换的目的
            var spinWait = new SpinWait();
            while (threadsWorking > 0)
            {
                Thread.MemoryBarrier();//内存屏障
                spinWait.SpinOnce();
            }
        }

        /// <summary>
        ///     清除所有返回的工作项缓存，并通过dispose方法返回“被重用”的工作项。
        /// </summary>
        public void ClearWorkItemCache()
        {
            WorkItem w;
            while(returnedWorkItems.TryDequeue(out w))
            {}
        }

        /// <summary>
        ///    中止所有活动线程
        /// </summary>
        public void ShutDown()
        {
            // 1.停止处理新的工作项……
            lock (lockObjectShutDownSignaled)
            {
                shutDownSignaled = true;
            }

            // 向所有工作项发出关闭命令…
            if (threads.Count > 0)
            {
                foreach (var thread in threads)
                {
                    thread.SignalShutDown();
                }
            }
        }

        /// <summary>
        ///    暂停所有活动线程。
        /// </summary>
        public void Sleep()
        {
            // 向所有工作项发出暂停命令的信号…
            if (threads.Count > 0)
            {
                foreach (var thread in threads)
                {
                    thread.SignalPause();
                }
            }
        }

        /// <summary>
        ///    恢复所有活动线程。
        /// </summary>
        public void Wakeup()
        {
            // 向所有工作项发出恢复命令的信号…
            if (threads.Count > 0)
            {
                foreach (var thread in threads)
                {
                    thread.SignalResume();
                }
            }
        }
    }
}