• 热门专题

Thread善用SpinWait处理 执行绪空转 以利提升效能

作者:余小章  发布日期:2013-04-25 21:48:17
Tag标签:Thread  SpinWait  
  • 当我们在处理一个执行绪时,若需要同步等待时,以往可能会常用 Thread.Sleep,但 Thread.Sleep 会消耗 CPU 的时间配置,所以我们可以使用 Thread.SpinWait 方法 、SpinWait 结构

    在 .NET4.0 以前,可以使用 Thread.SpinWait 方法

    下图出自http://msdn.microsoft.com/zh-tw/library/system.threading.thread.spinwait.aspx

     


     

    在 .NET4.0 以后,可以使用 SpinWait 结构

    下图出自http://msdn.microsoft.com/zh-tw/library/ee722114.aspx

     




     


    看来,MSDN 则是建议使用 SpinWait 结构 

    来看个例子:

    以往我常用 Stopwatch 来搭配 Thread.Sleep 来达到空转等待的目的

     

    public void Start(Action action)
    {
        if (this.IsRunning)
        {
            return;
        }
    
        this.IsRunning = true;
    
        Task.Factory.StartNew(() =>
        {
            Stopwatch watch = new Stopwatch();
            while (this.IsRunning)
            {
                watch.Restart();
                action.Invoke();
    
                while (watch.ElapsedMilliseconds < this.Interval)
                {
                    Thread.Sleep(1);
                }
            }
        });
    }

    把 this.Interval = 1 观察工作管理员结果,CPU 大约用掉了 2% (这在不同的机器会有不同的结果)。

     


     

    若是把 Thread.Sleep(1) 拿掉,CPU 负荷将近半载

     


     

    现在则把 Thread.Sleep拿掉,改用 SpinWait.SpinUntil 来运行空转等待。

     


     

    第一个参数是离开空转的条件,第二个参数是离开空转的时间,只要任一参数满足,则离开空转。

     

    public void Start(Action action)
    {
        if (this.IsRunning)
        {
            return;
        }
    
        this.IsRunning = true;
    
        Task.Factory.StartNew(() =>
        {
            while (this.IsRunning)
            {
                action.Invoke();
                SpinWait.SpinUntil(() => !this.IsRunning, this.Interval);
            }
        });
    }

    我们同样用 this.Interval = 1 来观察空转的效果,结果是 0%,很明显的这样的写法的确是胜于上一个方法。

     


     

    结论:

    PS.基本上 UI 更新的越快CPU飙的越高,若没有空转 UI 没办法更新。 www.it165.net

    所以我们可以把 Thread.Sleep(1) 可以换成 SpinWait.SpinUntil(() => false, 1)

    完整程式码,它没有防呆,请不要将 TextBox 设成 0 或空。


    public class Polling
    {
        private int _interval = 1000;
    
        public int Interval
            {
                get { return _interval; }
                set { _interval = value; }
            }
        public bool IsRunning { get; internal set; }
    
        public void Start(Action action)
        {
            if (this.IsRunning)
            {
                return;
            }
    
            this.IsRunning = true;
    
            Task.Factory.StartNew(() =>
            {
                while (this.IsRunning)
                {
                    action.Invoke();
                    SpinWait.SpinUntil(() => !this.IsRunning, this.Interval);
                }
            });
        }
    
        public void Stop()
        {
            if (!this.IsRunning)
            {
                return;
            }
            this.IsRunning = false;
        }
    }

    建立Winform专案,建立以下控制项

     


     

    用户端,调用方式,这里是使用 SynchronizationContext 更新 UI

     

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            m_SynchronizationContext = SynchronizationContext.Current;
        }
    
        private SynchronizationContext m_SynchronizationContext;
    
        private Polling _polling = new Polling();
        private int _counter = 0;
    
        private void button1_Click(object sender, EventArgs e)
        {
            this._polling.Interval = int.Parse(this.textBox1.Text);
            this._polling.Start(() =>
            {
                this._counter++;
                m_SynchronizationContext.Post(a => { this.label1.Text = this._counter.ToString(); }, null);
            });
        }
    
        private void button2_Click(object sender, EventArgs e)
        {
            this._polling.Stop();
        }
    }

     

About IT165 - 广告服务 - 隐私声明 - 版权申明 - 免责条款 - 网站地图 - 网友投稿 - 联系方式
本站内容来自于互联网,仅供用于网络技术学习,学习中请遵循相关法律法规