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.线程 ...
随机推荐
- 学习OpenCV——SVM 手写数字检测
转自http://blog.csdn.net/firefight/article/details/6452188 是MNIST手写数字图片库:http://code.google.com/p/supp ...
- C# 实现 微软WebRequestMethods.Ftp类中的FTP操作功能
先奉献一个测试地址,ftp内的文件请勿删除,谢谢 FtpEntity fe = "); 由于代码量较多,只抽出一部分,详细代码请移步 ftp://wjshan0808.3vhost.net ...
- java权限修饰符
- BZOJ 2594: [Wc2006]水管局长数据加强版(kruskal + LCT)
Description SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一 ...
- android webview 底层实现的逻辑
其实在不同版本上,webview底层是有所不同的. 先提供个地址给大家查:http://grepcode.com/file/repository.grepcode.com/java/ext/com.g ...
- Repeater嵌套绑定Repeater
前台Html代码 <asp:Repeater runat="server" ID="rpList" OnItemDataBound="rpLis ...
- FastJSON 之bean列表转换为JSON
实例 import java.util.ArrayList; import java.util.List; import com.alibaba.fastjson.JSON; public class ...
- Elasticsearch(入门篇)——Query DSL与查询行为
ES提供了丰富多彩的查询接口,可以满足各种各样的查询要求.更多内容请参考:ELK修炼之道 Query DSL结构化查询 Query DSL是一个Java开源框架用于构建类型安全的SQL查询语句.采用A ...
- ruby注意点
1.强类型,即不会自动进行类型转换,而C/C++为弱类型. # Ruby i = 1 puts "Value is " + i # TypeError: can't convert ...
- 为您详细比较三个 CSS 预处理器(框架):Sass、LESS 和 Stylus
CSS 预处理器技术已经非常的成熟,而且也涌现出了越来越多的 CSS 的预处理器框架.本文向你介绍使用最为普遍的三款 CSS 预处理器框架,分别是 Sass.Less CSS.Stylus. 首先我们 ...