c#网络通信框架networkcomms内核解析之十 支持优先级的自定义线程池
本例基于networkcomms2.3.1开源版本 gplv3协议
如果networkcomms是一顶皇冠,那么CommsThreadPool(自定义线程池)就是皇冠上的明珠了,这样说应该不夸张的,她那么优美,简洁,高效。
在 《c#网络通信框架networkcomms内核解析之六 处理接收到的二进制数据》中我们曾经提到,服务器收到数据后,如果是系统内部保留类型数据或者是最高优先级数据,系统会在主线程中处理,其他的会交给自定义线程池进行处理。
作为服务器,处理成千上万的连接及数据,单线程性能肯定是不行的,所以我们的支持优先级的自定义线程池毫无疑问是多线程的:)
注释就不写了,她美丽的面纱还是由您来揭开好了
// Copyright 2011-2013 Marc Fletcher, Matthew Dean // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // // A commercial license of this software can also be purchased. // Please see <http://www.networkcomms.net/licensing/> for details. using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Threading; namespace NetworkCommsDotNet { /// <summary> /// A compact thread pool used by NetworkComms.Net to run packet handlers /// </summary> public class CommsThreadPool { /// <summary> /// A sync object to make things thread safe /// </summary> object SyncRoot = new object(); /// <summary> /// Dictionary of threads, index is ThreadId /// </summary> Dictionary<int, Thread> threadDict = new Dictionary<int,Thread>(); /// <summary> /// Dictionary of thread worker info, index is ThreadId /// </summary> Dictionary<int, WorkerInfo> workerInfoDict = new Dictionary<int, WorkerInfo>(); /// <summary> /// The minimum timespan between thread wait sleep join updates /// </summary> TimeSpan ThreadWaitSleepJoinCountUpdateInterval = , , , , ); /// <summary> /// A quick lookup of the number of current threads which are idle and require jobs /// </summary> ; /// <summary> /// Priority queue used to order call backs /// </summary> PriorityQueue<WaitCallBackWrapper> jobQueue = new PriorityQueue<WaitCallBackWrapper>(); /// <summary> /// Set to true to ensure correct shutdown of worker threads. /// </summary> bool shutdown = false; /// <summary> /// The timespan after which an idle thread will close /// </summary> TimeSpan ThreadIdleTimeoutClose { get; set; } /// <summary> /// The maximum number of threads to create in the pool /// </summary> public int MaxTotalThreadsCount {get; private set;} /// <summary> /// The maximum number of active threads in the pool. This can be less than MaxTotalThreadsCount, taking account of waiting threads. /// </summary> public int MaxActiveThreadsCount { get; private set; } /// <summary> /// The minimum number of idle threads to maintain in the pool /// </summary> public int MinThreadsCount {get; private set;} /// <summary> /// The most recent count of pool threads which are waiting for IO /// </summary> public int CurrentNumWaitSleepJoinThreadsCache { get; private set; } /// <summary> /// The dateTime associated with the most recent count of pool threads which are waiting for IO /// </summary> public DateTime LastThreadWaitSleepJoinCountCacheUpdate { get; private set; } /// <summary> /// The total number of threads currently in the thread pool /// </summary> public int CurrentNumTotalThreads { get { lock(SyncRoot) return threadDict.Count; } } /// <summary> /// The total number of idle threads currently in the thread pool /// </summary> public int CurrentNumIdleThreads { get { lock (SyncRoot) return requireJobThreadsCount; } } /// <summary> /// The total number of items currently waiting to be collected by a thread /// </summary> public int QueueCount { get { return jobQueue.Count; } } /// <summary> /// Create a new comms thread pool /// </summary> /// <param name="minThreadsCount">Minimum number of idle threads to maintain in the pool</param> /// <param name="maxActiveThreadsCount">The maximum number of active (i.e. not waiting for IO) threads</param> /// <param name="maxTotalThreadsCount">Maximum number of threads to create in the pool</param> /// <param name="threadIdleTimeoutClose">Timespan after which an idle thread will close</param> public CommsThreadPool(int minThreadsCount, int maxActiveThreadsCount, int maxTotalThreadsCount, TimeSpan threadIdleTimeoutClose) { MinThreadsCount = minThreadsCount; MaxTotalThreadsCount = maxTotalThreadsCount; MaxActiveThreadsCount = maxActiveThreadsCount; ThreadIdleTimeoutClose = threadIdleTimeoutClose; } /// <summary> /// Prevent any additional threads from starting. Returns immediately. /// </summary> public void BeginShutdown() { lock(SyncRoot) shutdown = true; } /// <summary> /// Prevent any additional threads from starting and return once all existing workers have completed. /// </summary> /// <param name="threadShutdownTimeoutMS"></param> ) { List<Thread> allWorkerThreads = new List<Thread>(); lock(SyncRoot) { foreach (var thread in threadDict) { workerInfoDict[thread.Key].ThreadSignal.Set(); allWorkerThreads.Add(thread.Value); } } //Wait for all threads to finish foreach (Thread thread in allWorkerThreads) { try { if (!thread.Join(threadShutdownTimeoutMS)) thread.Abort(); } catch (Exception ex) { NetworkComms.LogError(ex, "ManagedThreadPoolShutdownError"); } } lock (SyncRoot) { jobQueue.Clear(); shutdown = false; } } /// <summary> /// Enqueue a callback to the thread pool. /// </summary> /// <param name="priority">The priority with which to enqueue the provided callback</param> /// <param name="callback">The callback to execute</param> /// <param name="state">The state parameter to pass to the callback when executed</param> /// <returns>Returns the managed threadId running the callback if one was available, otherwise -1</returns> public int EnqueueItem(QueueItemPriority priority, WaitCallback callback, object state) { ; lock (SyncRoot) { UpdateThreadWaitSleepJoinCountCache(); , threadDict.Count - CurrentNumWaitSleepJoinThreadsCache - requireJobThreadsCount); //int numActiveThreads = Math.Max(0,threadDict.Count - CurrentNumWaitSleepJoinThreadsCache); && numInJobActiveThreadsCount < MaxActiveThreadsCount && threadDict.Count < MaxTotalThreadsCount) { //Launch a new thread Thread newThread = new Thread(ThreadWorker); newThread.Name = "ManagedThreadPool_" + newThread.ManagedThreadId.ToString(); WorkerInfo info = new WorkerInfo(newThread.ManagedThreadId, new WaitCallBackWrapper(callback, state)); chosenThreadId = newThread.ManagedThreadId; threadDict.Add(newThread.ManagedThreadId, newThread); workerInfoDict.Add(newThread.ManagedThreadId, info); newThread.Start(info); } && numInJobActiveThreadsCount < MaxActiveThreadsCount) { jobQueue.TryAdd(new KeyValuePair<QueueItemPriority, WaitCallBackWrapper>(priority, new WaitCallBackWrapper(callback, state))); ; foreach (var info in workerInfoDict) { //Trigger the first idle thread checkCount++; if (info.Value.ThreadIdle) { info.Value.ClearThreadIdle(); requireJobThreadsCount--; info.Value.ThreadSignal.Set(); chosenThreadId = info.Value.ThreadId; break; } if (checkCount == workerInfoDict.Count) throw new Exception("IdleThreads count is " + requireJobThreadsCount.ToString() + " but unable to locate thread marked as idle."); } } else if (!shutdown) { //If there are no idle threads and we can't start any new ones we just have to enqueue the item jobQueue.TryAdd(new KeyValuePair<QueueItemPriority, WaitCallBackWrapper>(priority, new WaitCallBackWrapper(callback, state))); } } return chosenThreadId; } /// <summary> /// The worker object for the thread pool /// </summary> /// <param name="state"></param> private void ThreadWorker(object state) { WorkerInfo threadInfo = (WorkerInfo)state; do { //While there are jobs in the queue process the jobs while (true) { if (threadInfo.CurrentCallBackWrapper == null) { KeyValuePair<QueueItemPriority, WaitCallBackWrapper> packetQueueItem; lock (SyncRoot) { UpdateThreadWaitSleepJoinCountCache(); , threadDict.Count - CurrentNumWaitSleepJoinThreadsCache - requireJobThreadsCount); if (shutdown || threadDict.Count > MaxTotalThreadsCount) //If we have too many active threads { //If shutdown was true then we may need to set thread to idle ) requireJobThreadsCount--; threadInfo.ClearThreadIdle(); threadDict.Remove(threadInfo.ThreadId); workerInfoDict.Remove(threadInfo.ThreadId); UpdateThreadWaitSleepJoinCountCache(); return; } else if (numInJobActiveThreadsCount > MaxActiveThreadsCount) //If we have too many active threads { //We wont close here to prevent thread creation/destruction thrashing. //We will instead act as if there is no work and wait to potentially be timed out if (!threadInfo.ThreadIdle) { threadInfo.SetThreadIdle(); requireJobThreadsCount++; } break; } else { //Try to get a job if (!jobQueue.TryTake(out packetQueueItem)) //We fail to get a new job { //If we failed to get a job we switch to idle and wait to be triggered if (!threadInfo.ThreadIdle) { threadInfo.SetThreadIdle(); requireJobThreadsCount++; } break; } else { ) requireJobThreadsCount--; threadInfo.UpdateCurrentCallBackWrapper(packetQueueItem.Value); threadInfo.ClearThreadIdle(); } } } } //Perform the waitcallBack try { threadInfo.SetInsideCallBack(); threadInfo.CurrentCallBackWrapper.WaitCallBack(threadInfo.CurrentCallBackWrapper.State); } catch (Exception ex) { NetworkComms.LogError(ex, "ManagedThreadPoolCallBackError", "An unhandled exception was caught while processing a callback. Make sure to catch errors in callbacks to prevent this error file being produced."); } finally { threadInfo.ClearInsideCallBack(); } threadInfo.UpdateLastActiveTime(); threadInfo.ClearCallBackWrapper(); } //As soon as the queue is empty we wait until perhaps close time )) { //While we are waiting we check to see if we need to close if (DateTime.Now - threadInfo.LastActiveTime > ThreadIdleTimeoutClose) { lock (SyncRoot) { //We have timed out but we don't go below the minimum if (threadDict.Count > MinThreadsCount) { ) requireJobThreadsCount--; threadInfo.ClearThreadIdle(); threadDict.Remove(threadInfo.ThreadId); workerInfoDict.Remove(threadInfo.ThreadId); UpdateThreadWaitSleepJoinCountCache(); return; } } } } //We only leave via one of our possible breaks } while (true); } /// <summary> /// Returns the total number of threads in the pool which are waiting for IO /// </summary> private void UpdateThreadWaitSleepJoinCountCache() { lock (SyncRoot) { if (DateTime.Now - LastThreadWaitSleepJoinCountCacheUpdate > ThreadWaitSleepJoinCountUpdateInterval) { ; foreach (var thread in threadDict) { if (workerInfoDict[thread.Key].InsideCallBack && thread.Value.ThreadState == ThreadState.WaitSleepJoin) returnValue++; } CurrentNumWaitSleepJoinThreadsCache = returnValue; LastThreadWaitSleepJoinCountCacheUpdate = DateTime.Now; } } } /// <summary> /// Provides a brief string summarisation the state of the thread pool /// </summary> /// <returns></returns> public override string ToString() { lock (SyncRoot) { UpdateThreadWaitSleepJoinCountCache(); return "TotalTs:" + CurrentNumTotalThreads.ToString() + ", IdleTs:" + CurrentNumIdleThreads.ToString() + ", SleepTs:" + CurrentNumWaitSleepJoinThreadsCache.ToString() + ", Q:" + QueueCount.ToString(); } } } class WorkerInfo { public int ThreadId { get; private set; } public AutoResetEvent ThreadSignal { get; private set; } public bool ThreadIdle { get; private set; } public DateTime LastActiveTime {get; private set;} public WaitCallBackWrapper CurrentCallBackWrapper { get; private set; } public bool InsideCallBack { get; private set; } public WorkerInfo(int threadId, WaitCallBackWrapper initialisationCallBackWrapper) { ThreadSignal = new AutoResetEvent(false); ThreadIdle = false; ThreadId = threadId; LastActiveTime = DateTime.Now; this.CurrentCallBackWrapper = initialisationCallBackWrapper; } public void UpdateCurrentCallBackWrapper(WaitCallBackWrapper waitCallBackWrapper) { CurrentCallBackWrapper = waitCallBackWrapper; } public void UpdateLastActiveTime() { LastActiveTime = DateTime.Now; } public void ClearCallBackWrapper() { CurrentCallBackWrapper = null; } /// <summary> /// Set InsideCallBack to true /// </summary> public void SetInsideCallBack() { InsideCallBack = true; } /// <summary> /// Set InsideCallBack to false /// </summary> public void ClearInsideCallBack() { InsideCallBack = false; } /// <summary> /// Set threadIdle to true /// </summary> public void SetThreadIdle() { this.ThreadIdle = true; } /// <summary> /// Set threadIdle to false /// </summary> public void ClearThreadIdle() { this.ThreadIdle = false; } } /// <summary> /// A private wrapper used by CommsThreadPool /// </summary> class WaitCallBackWrapper { public WaitCallback WaitCallBack { get; private set; } public object State { get; private set; } public WaitCallBackWrapper(WaitCallback waitCallBack, object state) { this.WaitCallBack = waitCallBack; this.State = state; } } }
本例基于networkcomms2.3.1开源版本 gplv3协议
www.networkcomms.cn
www.cnblogs.com/networkcomms
c#网络通信框架networkcomms内核解析之十 支持优先级的自定义线程池的更多相关文章
- c#网络通信框架networkcomms内核解析 序言
NetworkComms网络通信框架序言 networkcomms是我遇到的写的最优美的代码,很喜欢,推荐给大家:) 基于networkcomms2.3.1开源版本( gplv3)协议,写了一些文章, ...
- c#网络通信框架networkcomms内核解析之八 数据包的核心处理器
NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本 gplv3协议 我们先回顾一个 c#网络通信框架networkcomms内核解析之六 处理接收到的二进制 ...
- c#网络通信框架networkcomms内核解析之六 处理接收到的二进制数据
本文基于networkcomms2.3.1开源版本 gplv3协议 在networkcomms通信系统中,服务器端收到某连接上的数据后,数据会暂时存放在"数据包创建器"(Pack ...
- 介绍开源的.net通信框架NetworkComms框架 源码分析(十五 ) CommsThreadPool自定义线程池
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...
- c#网络通信框架networkcomms内核解析之一 消息传送
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...
- c#网络通信框架networkcomms内核解析之一 消息传送2
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 在网络通信程序中,本地的类或者对象,要传输 ...
- c#网络通信框架networkcomms内核解析之三 消息同步调用
networkcomms.net 来自英国的网络通信框架 官方网址 www.networkcomms.net 中文网址www.networkcomms.cn 客户端发送消息给服务器,服务器计算结果返回 ...
- c#网络通信框架networkcomms内核解析之九 自定义处理方法的运行机制
NetworkComms网络通信框架序言 本文基于networkcomms2.3.1开源版本 gplv3协议 我们自己写的处理方法都称之为自定义处理方法 比如,我们在服务器上写的与登陆相关的处理方法 ...
- Java多线程和并发(十二),Java线程池
目录 1.利用Executors创建线程的五种不同方式 2.为什么要使用线程池 3.Executor的框架 4.J.U.C的三个Executor接口 5.ThreadPoolExecutor 6.线程 ...
随机推荐
- 20145320 《Java程序设计》第5周学习总结
20145320 <Java程序设计>第5周学习总结 教材学习内容总结 8.1 语法与继承架构 try.catch Java中的错误会被包装为对象,而使用try与catch,JVM会执行t ...
- Unity3D 装备系统学习Inventory Pro 2.1.2 总结
前言 写在最前面,本文未必适合纯新手,但有一些C#开发经验的还是可以看懂的,虽然本人也是一位Unity3D新人,但是本文只是自己在学习Inventory Pro的学习总结,而不是教程,本人觉得要读懂理 ...
- biztalk重新发布
前提:在vs2013中,项目属性:重新部署设置为true,重新启动主机实例:设置为true,或者在最后重新部署完以后手动重启主机实例 下面是具体的步骤: 1. 项目修改完重新生成.. 2. 转到biz ...
- jsp 环境配置记录
1. jdk,下载地址1 环境变量配置: 1)新建 JAVA_HOME 变量 . 变量值填写jdk的安装目录(本人是 C:\Java\jdk1.7.0) 2) 系统变量→寻找 Path 变量→编辑 ...
- JVM常见配置汇总
堆设置 -Xms:初始堆大小 -Xmx:最大堆大小 -XX:NewSize=n:设置年轻代大小 -XX:NewRatio=n:设置年轻代和年老代的比值.如:为3,表示年轻代与年老代比值为1:3,年轻代 ...
- 夺命雷公狗-----React---7--组建的状态props和state
props:组建初始要渲染的数据,他是不可以改变的 state:组建状态发生改变,调用render重新渲染数据 我们来写一个例子: <!DOCTYPE html> <html lan ...
- for循环使用详解(c语言版)
说明:c语言的很多语法部分都和JAVA,AS等相同 特别是for的用法. c语言中的for循环语句使用最为灵活,不仅可以用于循环次数已经确定的情况,而且可以用于循环次数不确定而只给出循环结束条件的情况 ...
- VS 编辑并继续(转载)
转]Microsoft Visual Studio vs2008 vs2010 调试 编辑 修改 代码 在vs2008的文件菜单下,前两个菜单项分别是新建项目 和 新建网站. 这两项里,都可以建web ...
- Android中插件开发篇之----类加载器
关于插件,已经在各大平台上出现过很多,eclipse插件.chrome插件.3dmax插件,所有这些插件大概都为了在一个主程序中实现比较通用的功能,把业务相关或者让可以让用户自定义扩展的功能不附加在主 ...
- SQL 向上取整、向下取整、四舍五入取整的实例!round、rounddown、roundup
sql server ==================================================== [四舍五入取整截取] select round(54.56,0) === ...