Thread.Sleep导致处理延时的情况
Thread.Sleep的主要功能是让线程挂起一段时间,而指定的时候可以是毫秒为单位,由于windows操作并不是实时操作系统,所以当Thread.Sleep指定的毫秒数少于15以下那基本是不太可能在这个时间内恢复线程到工作状态。既然是这样那在设计队列的时候就不得不面对一个处理延时的问题,首先看下以下代码:
public virtual void Run() { while (!mDispose) { OnRun(); } } protected virtual void OnRun() { item = GetItem(); if (item != null) { try { using (item) { item.Execute(); } } catch { } } else System.Threading.Thread.Sleep(SleepMS); }
以上是一个简单的队列实现,当前队列不存在工作的时候把当前线程Sleep一段时间,当队列线程进行挂起后的时候有消息刚好进来,那这个消息不得不在15ms后才能被处理。也许这个延时只是很小完全可以接受,看一下以一个程序的实现。
以上是一个简单的数据接收,逻辑处理和发送的调度过程。其环节存在着两个队列调度处理时,按上面所提到的情况来看这个处理的过程很有可能出现在处理上存在着几十毫秒的延时,而这个延时的出现并不是处理资源不足导致,而是资源过剩所产生的结果。为什么这样说呢,因为只有队列空闲的情况下队列线程才存在挂起的情况……
其实当队列处于满负载的情况线程挂起操作是不可能存在的,那如何去解决以上问题呢,其实可以使用Sleep(0)不让线程挂指定时间,直接告诉操作系统把位置让出来后马上进入排队,这样可以保证处理延时缩到最短,当然也可以SpinWait。不过以上两种解决方式都是比较抢占系统资源,当你的系统资源不充足的情况那还是不要采用。
实际情况可以采用缓解的手段来处理以上问题,把两个队列的工作合并到一起,这样做的好处就是导致延时的环节少了,还有就是处理的东西压缩在一个线程上这样线程处于空闲的时间就更少。为了达到以上效果所以调度器和工作项设计就必须有一个规则约束,可以这样实现一个调度器
public interface IDespatchItem : IDisposable { void Execute(); } class Despatch : IDisposable { public Despatch() { System.Threading.ThreadPool.QueueUserWorkItem((o) => { Run(); }); } public int SleepMS { get; set; } private Queue<IDespatchItem> mQueues = new Queue<IDespatchItem>(1024); public void Add(IDespatchItem item) { lock (mQueues) { if (item == null) return; mQueues.Enqueue(item); } } public int Count { get { return mQueues.Count; } } protected IDespatchItem GetItem() { lock (mQueues) { if (mQueues.Count > 0) return mQueues.Dequeue(); return null; } } protected virtual void OnRun() { IDespatchItem item = GetItem(); if (item != null) { try { using (item) { item.Execute(); } } catch { } } else System.Threading.Thread.Sleep(SleepMS); } public virtual void Run() { while (!mDispose) { OnRun(); } } private bool mDispose = false; public void Dispose() { lock (this) { if (!mDispose) { while (mQueues.Count > 0) { mQueues.Dequeue().Dispose(); } mDispose = true; } } } }
访问Beetlex的Github