在项目中如果是web请求时候,IIS会自动分配一个线程来进行处理,如果很多个应用程序共享公用一个IIS的时候,线程分配可能会出现一个问题(当然也是我的需求造成的)

之前在做项目的时候,有一个需求,就是当程序启动的时候,希望能够启动一定数目的线程,然后每一个线程始终都是在运行的状态,不进行释放,然后循环去做一些事情。那么IIS的线程管理可能就不是我想要的,因为我想我的一些程序,只用我开启的线程来做工作。也就是说我想模拟一个线程池,每次有一个调用的时候从自定义线程池中取出一个,用完再放回去。

谈谈我的思路:

1.程序一启动就通过for循环来创建,一定数目的线程(这个数目是可以配置的)

2.至少要有三个容器来存储线程,分别是工作线程队列和空闲线程队列以及等待队列

3.使用线程中的AutoResetEvent类,初始每一个线程都是unsignaled状态,线程一启动就一直在循环调用WaitOne()方法,那么每次外部调用的时候,都调用一次这个类实例对象的set,线程然后就可以继续做下面的工作了。

4.至少两个方法:

第一个开放给外部,让外部的方法能够被传入执行,然后这个方法能够判断空闲队列,等待队列,以及工作队列的状态,如果传入的时候发现,空闲队列有空闲的线程就直接,将任务委托给空闲队列的一个线程执行,否则把它放到等待队列。

第二个方法,需要能够将工作完成的线程从工作队列移动到空闲队列,然后判断一下等待队列是不是有任务,有的话就交给空闲队列里面的线程来执行。

体思路如上,可以试试先写一下。

1.因为每个线程都有一个AutoResetEvent的实例,所以最好把Thread进行封装,变成我们自己的Thread。

  public class Task
{
#region Variable
//一个AutoResetEvent实例
private AutoResetEvent _locks = new AutoResetEvent(false);
//一个Thread实例
private Thread _thread;
// 绑定回调方法,就是外部实际执行的任务
public Action _taskAction; //定义一个事件用来绑定工作完成后的操作,也就是4中所说的工作队列向空闲队列移动
public event Action<Task> WorkComplete; /// <summary>
///设置线程拥有的Key
/// </summary>
public string Key { get; set; } #endregion //线程需要做的工作
private void Work()
{
while (true)
{
//判断信号状态,如果有set那么 _locks.WaitOne()后的程序就继续执行
_locks.WaitOne();
_taskAction();
//执行事件
WorkComplete(this);
}
} #region event
//构造函数
public Task()
{
//スレッドオブジェクトを初期化する
_thread = new Thread(Work);
_thread.IsBackground = true;
Key = Guid.NewGuid().ToString();
//线程开始执行
_thread.Start();
} //Set开起信号
public void Active()
{
_locks.Set();
} #endregion
}

解释:上面那个Key的作用,因为多个线程同时进行的时候,我们并不知道哪一个线程的工作先执行完,所以说上面的工作队列,实际上应该用一个字典来保存,这样我们就能在一个线程结束工作之后,通过这  里的KEY(每个线程不一样),来进行定位了。

2.线程封装好了,然后就可以实现线程池了

   public class TaskPool
{ #region Variable
//创建的线程数
private int _threadCount;
//空闲线程队列
private Queue<Task> _freeQueue;
//工作线程字典(为什么?)
private Dictionary<string, Task> _workingDictionary;
//空闲队列,存放需要被执行的外部函数
private Queue<Action> _waitQueue;
#endregion #region Event
//自定义线程池的构造函数
public TaskPool()
{
_workingDictionary = new Dictionary<string, Task>();
_freeQueue = new Queue<Task>();
_waitQueue = new Queue<Action>();
_threadCount = ; Task task = null;
//产生固定数目的线程
for (int i = ; i < _threadCount; i++)
{
task = new Task();
//给每一个任务绑定事件
task.WorkComplete += new Action<Task>(WorkComplete);
//将每一个新创建的线程放入空闲队列中
_freeQueue.Enqueue(task);
}
} //线程任务完成之后的工作
void WorkComplete(Task obj)
{
lock (this)
{
//将线程从字典中排除
_workingDictionary.Remove(obj.Key);
//将该线程放入空闲队列
_freeQueue.Enqueue(obj); //判断是否等待队列中有任务未完成
if (_waitQueue.Count > )
{
//取出一个任务
Action item = _waitQueue.Dequeue();
Task newTask = null;
//空闲队列中取出一个线程
newTask = _freeQueue.Dequeue();
// 线程执行任务
newTask._taskAction = item;
//把线程放入到工作队列当中
_workingDictionary.Add(newTask.Key, newTask);
//设置信号量
newTask.Active();
return;
}
else
{
return;
}
}
} //添加任务到线程池
public void AddTaskItem(Action taskItem)
{
lock (this)
{
Task task = null;
//判断空闲队列是否存在线程
if (_freeQueue.Count > )
{
//存在线程,取出一个线程
task = _freeQueue.Dequeue();
//将该线程放入工作队列
_workingDictionary.Add(task.Key, task);
//执行传入的任务
task._taskAction = taskItem;
//设置信号量
task.Active();
return;
}
else
{
//空闲队列中没有空闲线程,就把任务放到等待队列中
_waitQueue.Enqueue(taskItem);
return;
}
}
}
#endregion }

解释:这里的两个方法,基本符合我的设想,注意每一个方法里面都有lock操作,这就保证了,多个线程进行操作相同的队列对象的时候,能够进行互斥。保证一个时间只有一个线程在操作。

测试代码:

    class Program
{
static void Main(string[] args)
{
TaskPool _taskPool = new TaskPool(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
for (var i = ; i < ; i++)
{
_taskPool.AddTaskItem(Print);
}
Console.Read();
} public static void Print()
{
Console.WriteLine("Do Something!");
}
}
}

这里我执行了20次print操作,看看结果是啥:

从图中看到20次确实执行了,但是看不到线程是哪些,稍微修改一下自定义的线程池。

1.在自定义线程的构造函数中添加:如下代码,查看哪些线程被创建了

         public Task()
{
_thread = new Thread(Work);
_thread.IsBackground = true;
Key = Guid.NewGuid().ToString();
//线程开始执行
_thread.Start();
Console.WriteLine("Thread:"+_thread.ManagedThreadId+" has been created!");
}

2.在线程完成工作方法之后添加如下代码,查看哪些线程参与执行任务

         private void Work()
{
while (true)
{
//判断信号状态,如果有set那么 _locks.WaitOne()后的程序就继续执行
_locks.WaitOne();
_taskAction();
Console.WriteLine("Thread:" + Thread.CurrentThread.ManagedThreadId+"workComplete");
//执行事件
WorkComplete(this);
}
}

3.修改客户端程序

     class Program
{
static void Main(string[] args)
{
TaskPool _taskPool = new TaskPool(); for (var i = ; i < ; i++)
{
_taskPool.AddTaskItem(Print);
}
Console.Read();
} public static void Print()
{
Thread.Sleep();
} }

测试结果:

从结果可以看到,开始和执行的线程都是固定的那10个,所以这个程序是可用的。

一个自定义线程池的小Demo的更多相关文章

  1. SOFA 源码分析 — 自定义线程池原理

    前言 在 SOFA-RPC 的官方介绍里,介绍了自定义线程池,可以为指定服务设置一个独立的业务线程池,和 SOFARPC 自身的业务线程池是隔离的.多个服务可以共用一个独立的线程池. API使用方式如 ...

  2. Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池

    前言:由于最近在做SDK的功能,需要设计线程池.看了很多资料不知道从何开始着手,突然发现了AsyncTask有对线程池的封装,so,就拿它开刀,本文将从AsyncTask的基本用法,到简单的封装,再到 ...

  3. java多线程(四)-自定义线程池

    当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...

  4. SpringCloud 微服务中 @Async 注解自定义线程池 引发的aop 问题

    背景 在 使用springCloud 的@Async注解来做异步操作时,想自定义其线程池. 引发问题 自定义完线程池后,发现代码里并没有使用自定义线程池里的线程,于是新建一个demo工程,一样的配置代 ...

  5. 自定义线程池的名称(ThreadPoolExecutor)

    目的:有时候为了快速定位出现错误的位置,在采用线程池时我们需要自定义线程池的名称. 1.创建ThreadFactory(ThreadPoolExecutor默认采用的是DefaultThreadFac ...

  6. SpringBoot—自定义线程池及并发定时任务模板

    介绍   在项目开发中,经常遇到定时任务,今天通过自定义多线程池总结一下SpringBoot默认实现的定时任务机制. 定时任务模板 pom依赖 <dependencies> <dep ...

  7. Android线程管理之ThreadPoolExecutor自定义线程池

    前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...

  8. Android 自定义线程池的实战

    前言:在上一篇文章中我们讲到了AsyncTask的基本使用.AsyncTask的封装.AsyncTask 的串行/并行线程队列.自定义线程池.线程池的快速创建方式. 对线程池不了解的同学可以先看 An ...

  9. 介绍开源的.net通信框架NetworkComms框架 源码分析(十五 ) CommsThreadPool自定义线程池

    原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架  作者是英国人  以前是收费的 目前作者已经开源  许可是 ...

随机推荐

  1. 浅析基于微软SQL Server 2012 Parallel Data Warehouse的大数据解决方案

    作者 王枫发布于2014年2月19日 综述 随着越来越多的组织的数据从GB.TB级迈向PB级,标志着整个社会的信息化水平正在迈入新的时代 – 大数据时代.对海量数据的处理.分析能力,日益成为组织在这个 ...

  2. Error creating bean with name 'sessionFactory' defined in class path resource [applicationContext.xml]: Invocation of init method failed; neste

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'sessionFacto ...

  3. 关于.NET三层 分类: C#

    三层体系结构的概念 用户界面表示层(USL) 业务逻辑层(BLL) 数据访问层(DAL) BLL将USL与DAL隔开了,并且加入了业务规则 各层的作用 1:数据数据访问层:主要是对原始数据(数据库或者 ...

  4. Oracle11g客户端for centos 5.5安装文档

    前提:安装centos的时候,最好把系统的所有的安装包都安装上.以下命令需手动输入,不要复制粘贴. 1.以root用户登录centos5.5的操作系统:创建oracle用户组和用户. groupadd ...

  5. Django自定义用户认证

    自定义一个用户认证 详细参考官方文档: https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#django.contrib.au ...

  6. Spark RDD/Core 编程 API入门系列之map、filter、textFile、cache、对Job输出结果进行升和降序、union、groupByKey、join、reduce、lookup(一)

    1.以本地模式实战map和filter 2.以集群模式实战textFile和cache 3.对Job输出结果进行升和降序 4.union 5.groupByKey 6.join 7.reduce 8. ...

  7. nyoj 116 士兵杀敌(二)【线段树单点更新+求和】

    士兵杀敌(二) 时间限制:1000 ms  |  内存限制:65535 KB 难度:5   描述 南将军手下有N个士兵,分别编号1到N,这些士兵的杀敌数都是已知的. 小工是南将军手下的军师,南将军经常 ...

  8. Code Forces 711D Directed Roads

    D. Directed Roads time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  9. Object类中getClass()

    Object类中包含一个方法名叫getClass,利用这个方法就可以获得一个实例的类型类.类型类指的是代表一个类型的类,因为一切皆是对象,类型也不例外,在Java使用类型类来表示一个类型.所有的类型类 ...

  10. eclipse中建立tomcat容器

    步骤 1.  new - orther - server 出现下图,选择tomcat版本, 2. 选择已有的web项目至tomcat容器中,如果尚未建立,可不选. 3. 点击完成后,就会发现一个新建项 ...