C#实现自定义线程池实例代码

在项目中如果是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 = 10;

            Task task = null;
            //产生固定数目的线程
            for (int i = 0; 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 > 0)
                {
                    //取出一个任务
                    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 > 0)
                {
                    //存在线程,取出一个线程
                    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 = 0; i < 20; 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 = 0; i < 20; i++)
            {
                _taskPool.AddTaskItem(Print);
            }
            Console.Read();
        }

        public static void Print()
        {
            Thread.Sleep(10000);
        }
    }

测试结果:

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

到此这篇关于C#自定义线程池的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#多线程之线程池ThreadPool用法

    目录 一.ThreadPool 1.QueueUserWorkItem() 2.GetMaxThreads() 3.GetMinThreads() 4.SetMaxThreads()和SetMinThreads() 二.线程等待 三.线程重用 一.ThreadPool ThreadPool是.Net Framework 2.0版本中出现的. ThreadPool出现的背景:Thread功能繁多,而且对线程数量没有管控,对于线程的开辟和销毁要消耗大量的资源.每次new一个THread都要重新开辟内

  • C#线程处理系列之线程池中的I/O线程

    一.I/O线程实现对文件的异步  1.1  I/O线程介绍: 对于线程所执行的任务来说,可以把线程分为两种类型:工作者线程和I/O线程. 工作者线程用来完成一些计算的任务,在任务执行的过程中,需要CPU不间断地处理,所以,在工作者线程的执行过程中,CPU和线程的资源是充分利用的. I/O线程主要用来完成输入和输出的工作的,在这种情况下, 计算机需要I/O设备完成输入和输出的任务,在处理过程中,CPU是不需要参与处理过程的,此时正在运行的线程将处于等待状态,只有等任务完成后才会有事可做, 这样就造

  • C#多线程ThreadPool线程池详解

    简单说明一下: 线程池可以看做容纳线程的容器:一个应用程序最多只能有一个线程池:ThreadPool静态类通过QueueUserWorkItem()方法将工作函数排入线程池: 每排入一个工作函数,就相当于请求创建一个线程: 线程池的作用: 1.线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率. 2.如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜.),况且我们还不能控制线程池中线程的开始.挂起.和

  • C#多线程之线程池(ThreadPool)

    一.简介 前面介绍了平时用到的大多数的多线程的例子,但在实际开发中使用的线程往往是大量的和更为复杂的,这时,每次都创建线程.启动线程.从性能上来讲,这样做并不理想(因为每使用一个线程就要创建一个,需要占用系统开销):从操作上来讲,每次都要启动,比较麻烦.为此引入的线程池的概念. 好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销 2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”. 在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.需要处理的

  • C#多线程之线程池ThreadPool详解

    一.ThreadPool概述 提供一个线程池,该线程池可用于执行任务.发送工作项.处理异步 I/O.代表其他线程等待以及处理计时器. 创建线程需要时间.如果有不同的小任务要完成,就可以事先创建许多线程/在应完成这些任务时发出请求.不需要自己创建这样一个列表.该列表由ThreadPool类托管. 这个类会在需要时增减池中线程的线程数,直到最大的线程数.池中的最大线程数是可配置的.在双核CPU中,默认设置为1023 个工作线程和1000个I/O线程.也可以指定在创建线程池时应立即启动的最小线程数,以

  • C#实现控制线程池最大数并发线程

    1. 实验目的: 使用线程池的时候,有时候需要考虑服务器的最大线程数目和程序最快执行所有业务逻辑的取舍. 并非逻辑线程越多也好,而且新的逻辑线程必须会在线程池的等待队列中等待 ,直到线程池中工作的线程执行完毕, 才会有系统线程取出等待队列中的逻辑线程,进行CPU运算. 2.  解决问题: <a>如果不考虑服务器实际可支持的最大并行线程个数,程序不停往线程池申请新的逻辑线程,这个时候我们可以发现CPU的使用率会不断飙升,并且内存.网络带宽占用也会随着逻辑线程在CPU队列中堆积,而不断增大. &l

  • C#多线程系列之线程池

    目录 线程池 ThreadPool 常用属性和方法 线程池说明和示例 线程池线程数 线程池线程数说明 不支持的线程池异步委托 任务取消功能 计时器 线程池 线程池全称为托管线程池,线程池受 .NET 通用语言运行时(CLR)管理,线程的生命周期由 CLR 处理,因此我们可以专注于实现任务,而不需要理会线程管理. 线程池的应用场景:任务并行库 (TPL)操作.异步 I/O 完成.计时器回调.注册的等待操作.使用委托的异步方法调用和套接字连接. 很多人不清楚 Task.Task<TResult>

  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    0.线程的本质 线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度. 1.多线程: 使用多个处理句柄同时对多个任务进行控制处理的一种技术.据博主的理解,多线程就是该应用的主线程任命其他多个线程去协助它完成需要的功能,并且主线程和协助线程是完全独立进行的.不知道这样说好不好理解,后面慢慢在使用中会有更加详细的讲解. 2.多线程的使用: (1)最简单.最原始的使用方法:Thread oGetArgThre

  • C#线程池用法详细介绍

    介绍 .NET Framework提供了包含ThreadPool类的System.Threading 空间,这是一个可直接访问的静态类,该类对线程池是必不可少的.它是公共"线程池"设计样式的实现.对于后台运行许多各不相同的任务是有用的.对于单个的后台线种而言有更好的选项. 线程的最大数量.这是完全无须知道的.在.NET中ThreadPool的所有要点是它自己在内部管理线程池中线程.多核机器将比以往的机器有更多的线程.微软如此陈述"线程池通常有一个线程的最大数量,如果所有的线程

  • C#实现线程池的简单示例

    本文以实例演示了C#线程池的简单实现方法.程序中定义了一个对象类,用以包装参数,实现多个参数的传递.成员属性包括两个输入参数和一个输出参数.代码简单易懂,备有注释便于理解. 具体实现代码如下: using System; using System.Threading; //定义对象类,用以包装参数,实现多个参数的传递 class Packet { //成员属性包括两个输入参数和一个输出参数 protected internal String inval1; protected internal

  • C#线程池操作方法

    本文实例讲述了C#线程池操作方法.分享给大家供大家参考.具体如下: static void Main(string[] args) { //设置线程池中的线程数最大为1000, //第一个为工作者线程,第二个为I/O线程 ThreadPool.SetMaxThreads(1000, 1000); for (int i = 0; i < 10;i ) { ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), string.Forma

  • C#线程池ThreadPool用法简介

    目录 一.ThreadPool概述 线程池使用起来很简单,但它有一些限制 使用线程池线程的操作的情况包括 二.方法 三.设置和获取线程数方法 四.将方法排入队列以便执行:QueueUserWorkItem(WaitCallback, Object) 五.RegisterWaitForSingleObject 注册等待句柄 1.参数 2.返回 一.ThreadPool概述 提供一个线程池,该线程池可用于执行任务.发送工作项.处理异步 I/O.代表其他线程等待以及处理计时器. 创建线程需要时间.如果

随机推荐