【C# 线程】【同步锁一】SpinWait类 -同步基元|同步原语

2022/1/1 9:08:05

本文主要是介绍【C# 线程】【同步锁一】SpinWait类 -同步基元|同步原语,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Spinwait类

完成代码:SpinWait.cs (dot.net)

原理:

SpinOnce()执行一次,执行次数超过10之后,每次进行自旋便会触发Thread.Yield()上下文切换的操作,在这之后每5次会进行一次sleep(0)操作,每20次会进行一次sleep(1)操作。SpinOnce()执行一次是大概7个时钟周期。第一自旋例外,第一次的时候比较耗时。

SpinOnce(int32 num) 执行num次后进入sleep(1); SpinOnce执行一次是大概7个时钟周期。第一例外,第一次的时候比较耗时。


Thread.Sleep(0) 表示让优先级高的线程插队,如果没有线程优先级更高线程插队,那么就继续执行。
Thread.Yield() 让有需要的线程先执行,忽略线程优先级(低级别的线程也可以在core运行),而该线程进入就绪队列。

SpinWait 内部用的是Thread.SpinWait(),Thread.SpinWait()调用外部函数(private static extern void SpinWaitInternal(int iterations);)现实自旋。

 

属性

count 表示自旋次数

isNextSpinWillYield :自旋超过10次,开始yield模式
源代码:

    public bool NextSpinWillYield
    {
        get
        {
            if (_count < 10)
            {
                return Environment.IsSingleProcessor;
            }
            return true;
        }
    }

方法

Reset();将count设置未0。

SpinOnce()执行一次,在次数超过10之后,每次进行便会触发Thread.Yield()上下文切换的操作,在这之后每5次会进行一次sleep(0)操作,每20次会进行一次sleep(1)操作。

SpinOnce(int32 num) 执行num次后进入sleep(1);SpinOnce方法都 thread.spin(nm).

 

核心代码

internal const int YieldThreshold = 10; // When to switch over to a true yield.
private const int Sleep0EveryHowManyYields = 5; // After how many yields should we Sleep(0)?
internal const int DefaultSleep1Threshold = 20; // After how many yields should we Sleep(1) frequently?

private void SpinOnceCore(int sleep1Threshold)
        {
          if ((
                    _count >= YieldThreshold &&
                    ((_count >= sleep1Threshold && sleep1Threshold >= 0) || (_count - YieldThreshold) % 2 == 0)
                ) ||
                Environment.IsSingleProcessor)
            {
       
 
                if (_count >= sleep1Threshold && sleep1Threshold >= 0)
                {
                    Thread.Sleep(1);
                }
                else
                {
                    int yieldsSoFar = _count >= YieldThreshold ? (_count - YieldThreshold) / 2 : _count;
                    if ((yieldsSoFar % Sleep0EveryHowManyYields) == (Sleep0EveryHowManyYields - 1))
                    {
                        Thread.Sleep(0);
                    }
                    else
                    {
                        Thread.Yield();
                    }
                }
            }
            else
            {
              
                int n = Thread.OptimalMaxSpinWaitsPerSpinIteration;
                if (_count <= 30 && (1 << _count) < n)
                {
                    n = 1 << _count;
                }
                Thread.SpinWait(n);
            }
 
            // Finally, increment our spin counter.
            _count = (_count == int.MaxValue ? YieldThreshold : _count + 1);
        }

 使用案例:

using System.Diagnostics;
using System.Reflection;

class Program
{

    static SpinLock sl = new();
    static void Main(string[] args)
    {

        
      
        Stopwatch stopwatch = new ();
        SpinWait sw = new();
  //每次自旋的时间
        for (int i = 0; i <40; i++)
        {
          
          //  sw.Reset();// SpinWait内部是 采用count 累计计数的,所以每次使用都要清零
            stopwatch.Reset();
            stopwatch.Start();
           
            sw.SpinOnce(31);
           
            stopwatch.Stop();
            
            Console.WriteLine(stopwatch.ElapsedTicks+sw.NextSpinWillYield.ToString()+"count:"+sw.Count);
        }


    }
}

SpinOnce()执行一次是大概7个时钟周期。第一例外,第一次的时候比较耗时。



这篇关于【C# 线程】【同步锁一】SpinWait类 -同步基元|同步原语的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程