高效的c#线程池设计
昨天写的 基于信号量Semaphore的c#线程池设计 在实际使用中,容易造成CPU占用偏高,笔者分析认为是由于信号量本身的缺点造成的,因此笔者重新设计了一款更高效的c#线程池,完全不使用waithandle之类的阻塞线程,而是使用更为简单的最大线程数值(一个long整数)来控制线程的运行。如果超过指定的线程数,那么保存在hash表中的线程进入等待队列,当有空闲位置时,才会释放出一个等待队列中的线程启动并运行。
笔者粗略地计算了下,此种方法比基于信号量的效率要高30左右,性能提高是显著的^_^,下面为大家展示代码。
public class MyThreadManager : MyThreadPool, iThreadManager { //线程池的启动、停止控制大同小异,不再赘述 private bool bStart; private bool bPause; private bool bStoped; public bool Running { get { return bStart && !bPause && !bStoped; } } public MyThreadManager(int num):base(num) { ControlCallback = new ThreadControl(Run); bStart = false; bPause = false; bStoped = false; } public virtual void Start() { bStart = true; bPause = false; bStoped = false; } public void Stop() { bPause = true; bStart = false; bStoped = false; } public void Pause() { bStoped = true; bStoped = false; bPause = false; } public void Resume() { bPause = false; bStoped = false; bStart = true; } public void Run() { while(!bStart || bPause || bStoped){ if (bStoped) { Thread.CurrentThread.Abort(); } Thread.Sleep(20); } while (Interlocked.Read(ref iRunningThreadNum) > Interlocked.Read(ref iMaxThreadNum)) { Thread.Sleep(20); } Interlocked.Increment(ref iRunningThreadNum); //增加线程池中运行的线程数量 } } public class MyThreadPool { protected Hashtable m_Pool; protected Queue<string> m_Queue; protected ThreadControl m_control; public ThreadControl ControlCallback { set { m_control = value; } get { return m_control; } } protected long iMaxThreadNum; public long MaxThreadNum { get { return Interlocked.Read(ref iMaxThreadNum); } } private long iQueueSize; protected long iRunningThreadNum; public long RunningThreadNum { get { return Interlocked.Read(ref iRunningThreadNum); } } public MyThreadPool(int num) { iMaxThreadNum = num; iRunningThreadNum = 0; iQueueSize = 0; m_Pool = new Hashtable(num); m_Queue = new Queue<string>(); } public void QueueWorkItem(ThreadCallback callback, object obj) { Thread thread = new Thread(delegate() { if (m_control != null) m_control(); callback(obj); //这里是实际执行的函数 m_Pool.Remove(Thread.CurrentThread.Name); //将当前线程移出线程池 Interlocked.Decrement(ref iRunningThreadNum); //运行线程数递减 ReleaseQueue(); //释放等待队列中的线程 }); string threadGuid = Guid.NewGuid().ToString(); thread.Name = threadGuid; //保证线程命名唯一 m_Pool.Add(threadGuid,thread); if (Interlocked.Read(ref iRunningThreadNum) < Interlocked.Read(ref iMaxThreadNum)) { thread.Start(); } else { m_Queue.Enqueue(thread.Name); Interlocked.Increment(ref iQueueSize); } } private void ReleaseQueue() //如果等待队列中有线程存在则释放一个,并做减计数 { if (Interlocked.Read(ref iQueueSize) > 0) { string threadname = ""; lock (m_Queue) { threadname = m_Queue.Dequeue(); Interlocked.Decrement(ref iQueueSize); } Thread t = m_Pool[threadname] as Thread; t.Start(); } } } public interface iThreadManager { void Start(); void Stop(); void Pause(); void Resume(); void Run(); } }
这种有得有失吧,虽然cpu不会太过偏高,但是任务队列有增无减(如监控等,系统负载不定的情况下),那么内存占用会逐渐增加直至溢出,症结在于损失了内存与cpu的自适应功能。
笔者现在的方案是把更多的精力放在资源的控制上,线程工作则交给系统底层来做,效果不错
请不要误导读者!
这根本就是不什么”高效的线程池”,你连线程池的本质都还没有理解!
如果您有高招,不吝赐教,笔者也是把学习中的一些经验写出来
在我的印象中 线程池最主要的一个特点是 里面的线程是可以复用的 而不是每次都要创建一个新的线程
早期作品,见笑了=_=!
你好,我是c#线程的初学者,看了你的文章,有点似懂非懂,我整了半天没整明白这个代码之间怎么管理的,可以请你写个例子供我研究一下吗?谢谢哈
你好,我写这段代码的本意是希望将多线程应用在海量web抓取中,不过就目前看来,这段代码的健壮性还远远不够。如果你只是学习,推荐你看看这篇文章:
《C#中的多线程》作者Joseph Albahari,翻译:Swanky Wu。另外,如果不是大型程序,建议直接ThreadPool就很可以了。这是我的一点看法^_^。