善用 SPINWAIT 处理 线程空转 以利提升效能

来源:互联网 发布:淘宝牛仔女裤 编辑:程序博客网 时间:2024/04/29 21:30

当我们在处理一个线程时,若需要同步等待时,以往可能会常用 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

 

下图出自http://msdn.microsoft.com/zh-tw/library/system.threading.spinwait.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 没办法更新。

所以我们可以把 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();    }}
2 0