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程序设计》第9周学习总结
20145320<Java程序设计>第9周学习总结 教材学习内容总结 16.整合数据库 JDBC(java DateBase Connectivity)是用于执行SQL的解决方案,开发人员 ...
- matlab实现分水岭算法处理图像分割
此程序为优化后的分水岭算法,避免了图像过分割 I= imread('D:\Images\pic_loc\1870405130305041503.jpg'); imshow(I); h=fspecial ...
- mssql手工注入及绕过术
报错注入: - 例子:http://www.kfgtfcj.xxx.cn/lzygg/Zixun_show.aspx?id=1 [1]首先爆版本:http://www.kfgtfcj.xxx.cn ...
- Spring中bean的scope详解
如何使用spring的作用域: <bean id="role" class="spring.chapter2.maryGame.Role" scope=& ...
- Android -- 使用ViewPager实现画廊效果
1,今天在微信推送文章看到实现画廊效果,感觉挺不错的,就来写写试试,先来看一下效果图: 上面的效果基本上可以用两个功能点来包含:ViewPager的切换动画.ImageView的倒影的实现 嗯,先来将 ...
- HTML5 webSQL 中查询结果集 result.rows.item 的用法
加入查询回调函数如下: function(tx,result){ var len = result.rows.length; var recordset = result.rows.item; ){ ...
- Java多线程编程——进阶篇二
一.线程的交互 a.线程交互的基础知识 线程交互知识点需要从java.lang.Object的类的三个方法来学习: void notify() 唤醒在此对象监视器上等待的单个 ...
- 简单破解.net(C#)程序
一直在用makedown2(free版),每当打开多个页面,就会提示升级为pro,还要注册码激活什么的.就有了破解的想法.以前也弄过一个小程序的破解,所以还算有些经验. 1. ildasm 用来将ma ...
- Dynamics AX 2012 R2 创建一个专用的批处理服务器
安装额外AOS的另一原因,是要创建一个专用的Batch服务器. AOS实例在处理batch job时,会影响它的响应速度.安装一个专用Batch服务器,可以解决这个问题. 批处理服务器不能加到 ...
- (4) 深入理解Java Class文件格式(三)
转载:http://blog.csdn.net/zhangjg_blog/article/details/21557357 首先, 让我们回顾一下关于class文件格式的之前两篇博客的主要内容. 在 ...