博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
一个自定义线程池的小Demo
阅读量:6712 次
发布时间:2019-06-25

本文共 6039 字,大约阅读时间需要 20 分钟。

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

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

谈谈我的思路:

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

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

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

4.至少两个方法:

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

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

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

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

1  public class Task 2     { 3         #region Variable 4         //一个AutoResetEvent实例 5         private AutoResetEvent _locks = new AutoResetEvent(false); 6         //一个Thread实例 7         private Thread _thread; 8         // 绑定回调方法,就是外部实际执行的任务                           9         public Action _taskAction;                             10 11         //定义一个事件用来绑定工作完成后的操作,也就是4中所说的工作队列向空闲队列移动12         public event Action
WorkComplete;13 14 ///
15 ///设置线程拥有的Key16 /// 17 public string Key { get; set; }18 19 #endregion20 21 //线程需要做的工作22 private void Work()23 {24 while (true)25 {26 //判断信号状态,如果有set那么 _locks.WaitOne()后的程序就继续执行27 _locks.WaitOne();28 _taskAction();29 //执行事件30 WorkComplete(this);31 }32 }33 34 #region event35 //构造函数36 public Task()37 {38 //スレッドオブジェクトを初期化する39 _thread = new Thread(Work);40 _thread.IsBackground = true;41 Key = Guid.NewGuid().ToString();42 //线程开始执行43 _thread.Start();44 }45 46 //Set开起信号47 public void Active()48 {49 _locks.Set();50 }51 52 #endregion53 }

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

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

1   public class TaskPool 2   { 3  4         #region Variable 5         //创建的线程数 6         private int _threadCount; 7         //空闲线程队列 8         private Queue
_freeQueue; 9 //工作线程字典(为什么?)10 private Dictionary
_workingDictionary;11 //空闲队列,存放需要被执行的外部函数12 private Queue
_waitQueue;13 #endregion14 15 #region Event16 //自定义线程池的构造函数17 public TaskPool()18 { 19 _workingDictionary = new Dictionary
();20 _freeQueue = new Queue
();21 _waitQueue = new Queue
();22 _threadCount = 10;23 24 Task task = null;25 //产生固定数目的线程26 for (int i = 0; i < _threadCount; i++)27 {28 task = new Task();29 //给每一个任务绑定事件30 task.WorkComplete += new Action
(WorkComplete); 31 //将每一个新创建的线程放入空闲队列中32 _freeQueue.Enqueue(task);33 }34 }35 36 //线程任务完成之后的工作37 void WorkComplete(Task obj)38 {39 lock (this)40 {41 //将线程从字典中排除42 _workingDictionary.Remove(obj.Key);43 //将该线程放入空闲队列44 _freeQueue.Enqueue(obj);45 46 //判断是否等待队列中有任务未完成47 if (_waitQueue.Count > 0)48 {49 //取出一个任务50 Action item = _waitQueue.Dequeue();51 Task newTask = null;52 //空闲队列中取出一个线程53 newTask = _freeQueue.Dequeue();54 // 线程执行任务55 newTask._taskAction = item;56 //把线程放入到工作队列当中57 _workingDictionary.Add(newTask.Key, newTask);58 //设置信号量59 newTask.Active();60 return;61 }62 else63 {64 return;65 }66 }67 }68 69 //添加任务到线程池70 public void AddTaskItem(Action taskItem)71 {72 lock (this)73 {74 Task task = null;75 //判断空闲队列是否存在线程76 if (_freeQueue.Count > 0)77 {78 //存在线程,取出一个线程79 task = _freeQueue.Dequeue();80 //将该线程放入工作队列81 _workingDictionary.Add(task.Key, task);82 //执行传入的任务83 task._taskAction = taskItem;84 //设置信号量85 task.Active();86 return;87 }88 else89 {90 //空闲队列中没有空闲线程,就把任务放到等待队列中91 _waitQueue.Enqueue(taskItem);92 return;93 }94 }95 }96 #endregion97 98 }

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

测试代码:

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

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

 

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

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

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

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

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

3.修改客户端程序

1     class Program 2     { 3         static void Main(string[] args) 4         { 5             TaskPool _taskPool = new TaskPool(); 6  7             for (var i = 0; i < 20; i++) 8             { 9                 _taskPool.AddTaskItem(Print);10             }11             Console.Read();12         }13 14         public static void Print()15         {16             Thread.Sleep(10000);17         }18 19     }

测试结果:

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

转载于:https://www.cnblogs.com/dcz2015/p/5263006.html

你可能感兴趣的文章
实现iOS图片等资源文件的热更新化(一): 从Images.xcassets导出合适的图片
查看>>
magento2 ajax机制 (customer-data)
查看>>
magento 2模块开发实例helloworld模块
查看>>
关于if-else流程图的画法
查看>>
calc 与 box-sizing 的替代
查看>>
如何使用 Java 构建微服务?
查看>>
kafka的SSL证书校验不通过
查看>>
glom模块的使用(二)
查看>>
Spring Boot 的 10 个核心模块
查看>>
我30岁了,转行学编程可以吗? 排除法告诉你答案
查看>>
Python进阶:全面解读高级特性之切片!
查看>>
社交大战升级,语音新物种“听途听app”获数百万天使轮融资
查看>>
C#并行开发_Thread/ThreadPool, Task/TaskFactory, Parallel
查看>>
主要的编程范型
查看>>
Centos-Mysql复制备份还原数据库
查看>>
FullPage.js全屏插件文档及使用方法
查看>>
keras 使用笔记
查看>>
Android中主要类的继承关系梳理汇总
查看>>
定制一款漂亮的终端
查看>>
(5)Python字典
查看>>