周末闲来无事,把思考了很久的一个线程池处理算法,实现了,总结了一下设计的思路和设计的初衷。
在进行多线程异步任务调用时,我们经常会用到.net中的线程池System.Threading.ThreadPool.QueueUserWorkItem,随着微软在线程池调度算法上的优化,线程池的效率和智能化等方面,越来越高,但是该线程池,也存在操作的缺陷,比如发起一个异步的大数据保存操作,执行数据保存可能需要耗时时间很长,在此过程中,用户可能发现前端数据输入有问题,需要取消操作,如果用默认的线程池,进行异步操作,由于线程池中的线程采用智能分配和调度,我们没法取消对应的线程操作,数据保存还在异步线程中执行着,最终得到不是需要的结果。
如果需要对任务可取消操作,又不丢失线程池的灵活性和方便性,我们需要自定义可执行任务取消处理的线程池,调用接口与原线程池一致,但是增加了取消任务的调用操作。
自定义线程的设计思路是,利用托管线程消息队列,负责所有的异步消息的分发和路由,同时利用托管线程消息队列,进行资源的管理,因为托管线程消息队列,已执行了消息的线程间同步,策略算法的处理,就不需要线程间的二次同步处理。
考虑到该线程池的场合,是分场景的,因此没有做成全局静态入口线程池,但可在调用处独立实例化封装为全局静态入口线程池。
整个设计流程思路:
核心调度算法代码,核心算法中用到了周期性的消息处理队列,这个在其他文章,介绍线程池时,有介绍,此处不再描述:
/// <summary>
/// 手工,带队列缓冲,可取消任务处理线程池
/// </summary>
public class ManualQueueThreadPool
{
/// <summary>
/// 容器托管线程
/// </summary>
private ITimerMessageFIFO dockerMessageFIFO = null;
/// <summary>
/// 等待被执行的任务列表
/// </summary>
private List<ManualTask> waitManualTaskList = new List<ManualTask>();
/// <summary>
/// 工作线程列表
/// </summary>
private List<WorkThreadInfo> workThreadInfoList = new List<WorkThreadInfo>();
/// <summary>
/// 最大工作线程数量
/// </summary>
private int maxWorkThreadCount = 10;
/// <summary>
/// 最大工作线程数量
/// </summary>
public int MaxWorkThreadCount
{
get
{
return maxWorkThreadCount;
}
}
/// <summary>
/// 最大的自由线程数量
/// </summary>
private int maxFreeThreadCount = 2;
/// <summary>
/// 最大空闲线程数量,空闲线程,用于随时执行新的调度
/// </summary>
public int MaxFreeThreadCount
{
get
{
return maxFreeThreadCount;
}
}
private int initWorkThreadCount = 3;
/// <summary>
/// 默认启动初始化线程数量
/// </summary>
public int InitWorkThreadCount
{
get
{
return initWorkThreadCount;
}
}
private int freeWorkThreadCollectTimeOut = 10;
/// <summary>
/// 空闲线程,回收处理超时时间,单位是分钟
/// </summary>
public int FreeWorkThreadCollectTimeOut
{
get
{
return freeWorkThreadCollectTimeOut;
}
}
/// <summary>
/// 是否正在关闭
/// </summary>
private volatile bool onClosing = false;
/// <summary>
/// 创建线程池
/// </summary>
/// <param name="initWorkThreadCount">默认启动初始化线程数量,默认3个</param>
/// <param name="maxFreeThreadCount">最大空闲线程数量,空闲线程,用于随时执行新的调度,默认2个</param>
/// <param name="maxWorkThreadCount">最大工作线程数量,默认10个</param>
/// <param name="freeWorkThreadCollectTimeOut">空闲线程回收超时时间,单位是分钟,默认10分钟</param>
/// <returns>
/// 返回创建的线程池
/// </returns>
public static ManualQueueThreadPool Create(int initWorkThreadCount = 3, int maxFreeThreadCount = 2, int maxWorkThreadCount = 10,int freeWorkThreadCollectTimeOut=10)
{
ManualQueueThreadPool manualQueueThreadPool = new ManualQueueThreadPool();
manualQueueThreadPool.initWorkThreadCount = initWorkThreadCount;
manualQueueThreadPool.maxFreeThreadCount = maxFreeThreadCount;
manualQueueThreadPool.maxWorkThreadCount = maxWorkThreadCount;
manualQueueThreadPool.freeWorkThreadCollectTimeOut = freeWorkThreadCollectTimeOut;
return manualQueueThreadPool;
}
private ManualQueueThreadPool()
{
}
/// <summary>
/// 启动
/// </summary>
public void Open()
{
this.InnerStartDocker();
ManualTaskCommandInfo taskCommandInfo = new ManualTaskCommandInfo(ManualTaskCommand.StartWorkThreads);
this.PushDockerMessageFIFO(taskCommandInfo);
}
/// <summary>
/// 关闭
/// </summary>
public void Close()
{
this.onClosing = true;
ManualTaskCommandInfo taskCommandInfo = new ManualTaskCommandInfo(ManualTaskCommand.CloseWorkThreads);
this.PushDockerMessageFIFO(taskCommandInfo);
this.InnerStopDocker();
}
/// <summary>
/// 内部启动
/// </summary>
private void InnerStartDocker()
{
if (dockerMessageFIFO == null)
{
dockerMessageFIFO = TimerMessageFIFO.Create(new ManualTimer.TimerParameters(200, 5000), MessageFIFOType.LockMessageFIFO);//5秒钟一次进行资源查询
dockerMessageFIFO.MessageCallBack += dockerMessageFIFO_MessageCallBack;
dockerMessageFIFO.SignalCallBack += dockerMessageFIFO_SignalCallBack;
}
if (dockerMessageFIFO != null)
{
if (dockerMessageFIFO.IsRunning() == false)
{
dockerMessageFIFO.Start();
}
}
}
/// <summary>
/// 启动工作线程列表
/// </summary>
private void InnerStartWorkThreadList()
{
for (int i = 0; i < initWorkThreadCount; i++)
{
IMessageFIFO workMessageFIFO = MessageFIFOFactory.Create(MessageFIFOType.LockMessageFIFO);
workMessageFIFO.Excute += workMessageFIFO_Excute;
workMessageFIFO.Start();
WorkThreadInfo workThreadInfo = new WorkThreadInfo(workMessageFIFO);
this.workThreadInfoList.Add(workThreadInfo);
}
}
/// <summary>
/// 执行具体的工作线程处理
/// </summary>
/// <param name="obj"></param>
private void workMessageFIFO_Excute(object obj)
{
if (obj is ManualTask)
{
ManualTask task = (ManualTask)obj;
task.WaitCallback.Invoke(task.Data);
}
}
/// <summary>
/// 停止工作线程列表
/// </summary>
private void InnerStopWorkThreadList()
{
if (this.workThreadInfoList==null || this.workThreadInfoList.Count == 0)
{
return;
}
for (int i = 0; i < this.workThreadInfoList.Count; i++)
{
WorkThreadInfo info = this.workThreadInfoList[i];
if (info.WorkThread != null && info.WorkThread.IsOpened == true)
{
info.WorkThread.ForceStop();
}
}
this.workThreadInfoList.Clear();
}
/// <summary>
/// 压入数据到内部消息处理队列
/// </summary>
/// <param name="data">数据</param>
private void PushDockerMessageFIFO(object data)
{
if (dockerMessageFIFO == null || dockerMessageFIFO.IsRunning() == false)
{
if (onClosing == false)
{
InnerStartDocker();
}
}
if (dockerMessageFIFO != null && dockerMessageFIFO.IsRunning() == true)
{
dockerMessageFIFO.PostData(data);
}
}
/// <summary>
/// 周期性的信号反馈,用于将缓存队列中的待处理任务,泵入工作线程,或对空闲线程进行释放
/// </summary>
/// <param name="sender"></param>
/// <param name="data"></param>
private void dockerMessageFIFO_SignalCallBack(object sender, bool data)
{
#region 释放掉,已关闭的工作线程
this.workThreadInfoList.RemoveAll(delegate(WorkThreadInfo tempWorkThreadInfo)
{
return tempWorkThreadInfo.WorkThread == null || tempWorkThreadInfo.WorkThread.IsOpened == false;
});
#endregion
#region 将缓存队列中的数据压入工作线程组
if (this.waitManualTaskList.Count > 0)//从缓存队列中获取
{
for (int i = 0; i < this.workThreadInfoList.Count; i++)
{
WorkThreadInfo tempWorkThreadInfo = this.workThreadInfoList[i];
if (tempWorkThreadInfo.WorkThread != null
&& tempWorkThreadInfo.WorkThread.IsOpened == true
&& tempWorkThreadInfo.WorkThread.WaitExcuteTaskCount <= 0)
{
if (this.waitManualTaskList.Count > 0)
{
ManualTask tempManualTask = this.waitManualTaskList[0];
tempWorkThreadInfo.PostTask(tempManualTask);
this.waitManualTaskList.Remove(tempManualTask);
}
else
{
break;
}
}
}
}
#endregion
#region 构建新的线程,用于处理缓存队列中的数据
if (this.waitManualTaskList.Count > 0)
{
if (this.workThreadInfoList.Count >= maxWorkThreadCount)//线程已达到最大用量,还存在未执行的任务
{
return;
}
for (int i = 0; i < this.waitManualTaskList.Count; i++)
{
ManualTask tempManualTask = this.waitManualTaskList[i];
if (this.workThreadInfoList.Count >= maxWorkThreadCount)
{
return;
}
IMessageFIFO workMessageFIFO = MessageFIFOFactory.Create(MessageFIFOType.LockMessageFIFO);
workMessageFIFO.Excute += workMessageFIFO_Excute;
workMessageFIFO.Start();
WorkThreadInfo newWorkThreadInfo = new WorkThreadInfo(workMessageFIFO);
this.workThreadInfoList.Add(newWorkThreadInfo);
newWorkThreadInfo.PostTask(tempManualTask);
}
}
#endregion
#region 线程使用未饱和,多余的空闲线程进行资源释放
int tempFreeThreadCount = 0;
List<WorkThreadInfo> freeWorkThreadInfoList = null;
for (int i = 0; i < this.workThreadInfoList.Count; i++)
{
WorkThreadInfo workThreadInfo = this.workThreadInfoList[i];
if (workThreadInfo.WorkThread != null
&& workThreadInfo.WorkThread.IsOpened == true
&& workThreadInfo.WorkThread.WaitExcuteTaskCount <= 0
&& (DateTime.Now - workThreadInfo.LastWorkTime).TotalMinutes > freeWorkThreadCollectTimeOut)
{
tempFreeThreadCount += 1;
if (this.workThreadInfoList.Count > (initWorkThreadCount + maxFreeThreadCount)
&& tempFreeThreadCount > maxFreeThreadCount)
{
if (freeWorkThreadInfoList == null)
{
freeWorkThreadInfoList = new List<WorkThreadInfo>();
}
freeWorkThreadInfoList.Add(workThreadInfo);
}
}
}
if (freeWorkThreadInfoList != null)
{
for (int i = 0; i < freeWorkThreadInfoList.Count; i++)
{
WorkThreadInfo workThreadInfo = freeWorkThreadInfoList[i];
workThreadInfo.WorkThread.Stop();
this.workThreadInfoList.Remove(workThreadInfo);
}
}
#endregion
}
/// <summary>
/// 消息处理回调
/// </summary>
/// <param name="sender"></param>
/// <param name="data"></param>
private void dockerMessageFIFO_MessageCallBack(object sender, object data)
{
if (data is ManualTask)//泵入任务到工作线程
{
ManualTask task = (ManualTask)data;
WorkThreadInfo workThreadInfo = null;
for (int i = 0; i < this.workThreadInfoList.Count; i++)
{
WorkThreadInfo tempWorkThreadInfo = this.workThreadInfoList[i];
if (tempWorkThreadInfo.WorkThread != null
&& tempWorkThreadInfo.WorkThread.IsOpened == true
&& tempWorkThreadInfo.WorkThread.WaitExcuteTaskCount <= 0)
{
workThreadInfo = tempWorkThreadInfo;
break;
}
}
if (workThreadInfo != null)
{
workThreadInfo.PostTask(task);
return;
}
if (workThreadInfoList.Count >= maxWorkThreadCount)
{
this.waitManualTaskList.Add(task);
return;
}
IMessageFIFO workMessageFIFO = MessageFIFOFactory.Create(MessageFIFOType.LockMessageFIFO);
workMessageFIFO.Excute += workMessageFIFO_Excute;
workMessageFIFO.Start();
WorkThreadInfo newWorkThreadInfo = new WorkThreadInfo(workMessageFIFO);
this.workThreadInfoList.Add(newWorkThreadInfo);
newWorkThreadInfo.PostTask(task);
}
else if (data is ManualTaskCommandInfo)
{
ManualTaskCommandInfo manualTaskCommandInfo = (ManualTaskCommandInfo)data;
if (manualTaskCommandInfo.ManualTaskCommand == ManualTaskCommand.StartWorkThreads)//初始化工作线程组
{
this.InnerStartWorkThreadList();
}
else if (manualTaskCommandInfo.ManualTaskCommand == ManualTaskCommand.CloseWorkThreads)//结束所有工作线程
{
this.InnerStopWorkThreadList();
}
else if (manualTaskCommandInfo.ManualTaskCommand == ManualTaskCommand.CancelTask)//取消某一个任务
{
if (this.waitManualTaskList.Count > 0)//从缓存队列中获取
{
ManualTask manualTask = this.waitManualTaskList.Find(delegate(ManualTask tempManualTask)
{
return tempManualTask.TaskID.Equals(manualTaskCommandInfo.TaskID, StringComparison.OrdinalIgnoreCase);
});
if (manualTask != null)
{
this.waitManualTaskList.Remove(manualTask);
return;
}
}
if (this.workThreadInfoList.Count > 0)//从工作线程组中获取
{
WorkThreadInfo workThreadInfo = this.workThreadInfoList.Find(delegate(WorkThreadInfo tempWorkThreadInfo)
{
return tempWorkThreadInfo.TaskID.Equals(manualTaskCommandInfo.TaskID, StringComparison.OrdinalIgnoreCase);
});
if (workThreadInfo != null)
{
workThreadInfo.WorkThread.ForceStop();
}
this.workThreadInfoList.Remove(workThreadInfo);
}
}
}
}
/// <summary>
/// 内部停止
/// </summary>
private void InnerStopDocker()
{
if (dockerMessageFIFO != null && dockerMessageFIFO.IsRunning() == true)
{
dockerMessageFIFO.Stop();
}
dockerMessageFIFO = null;
}
/// <summary>
/// 压入处理请求到线程池
/// </summary>
/// <param name="taskID">唯一任务ID</param>
/// <param name="waitCallBack">回调委托</param>
/// <param name="data">数据</param>
public void PushTaskToQueue(string taskID,WaitCallback waitCallBack,object data)
{
ManualTask task = new ManualTask(taskID, waitCallBack, data);
this.PushDockerMessageFIFO(task);
}
/// <summary>
/// 取消任务
/// </summary>
/// <param name="taskID">任务ID</param>
public void CancelTask(string taskID)
{
ManualTaskCommandInfo cancelManualTaskCommandInfo = new ManualTaskCommandInfo(ManualTaskCommand.CancelTask);
cancelManualTaskCommandInfo.TaskID = taskID;
this.PushDockerMessageFIFO(cancelManualTaskCommandInfo);
}
}
整个调度算法写完之后,用于替换,Trace.RMI.Server中间件中的,数据处理部分,具体应用场景为,客户端发起TCP会话连接,服务器端监听到连接会话时,将每一次的处理任务数据,发送到线程池,在线程池中具体执行,服务器端的业务处理,监听会话线程结束,监听会话回归连接池。回调时,在处理线程调用会话ID,重新将连接会话调入业务处理线程,进行回调结果处理。由于服务器端的业务处理,可能时间比较长,客户端在异步等待窗口等待,当用户因为等待时间太长,需要取消操作时,服务器端的业务处理,就无法通过连接会话的取消而取消了。新的场景中,通过对应的连接会话ID,传入处理线程池,取消连接会话对应的任务,进一步取消数据存储事务,而达到数据的回滚的目的。