【问题标题】:SpinLock doesn't really do busy-loop waiting?SpinLock 真的不做忙循环等待吗?
【发布时间】:2022-01-14 13:54:29
【问题描述】:

我有以下代码

class Program
    {
        static SpinLock sl = new SpinLock();

        public static void Foo(object arg)
        {
            bool r = false;
            sl.Enter(ref r);
        }
        public static void Main(string[] args)
        {
            bool r = false;
            sl.Enter(ref r);
            ThreadPool.QueueUserWorkItem(Foo);
            Thread.Sleep(5000);
            sl.Exit();
            Thread.Sleep(5000);
        }
    }

当我执行它时,我看到在整个程序运行期间大约有 0% 的 CPU 负载。由于在sl.Enter() 调用中旋转,我预计会看到一些 CPU 烧毁,但事实并非如此。

根据SpinLock source,它在底层使用SpinWait,而后者又在某些时候实际调用Thread.Sleep(1)

这是否意味着 SpinLockSpinWait 都不是真正的“微调器”,而是仍然依靠操作系统调度程序来产生一些 CPU 份额?

编辑

问题可以用以下方式重新表述:

使用忙循环可确保一个线程释放锁与另一个线程获取锁之间的时间最短。在这方面我可以依靠SpinLock/SpinWait吗?据我所知,答案是,因为Sleep 至少持续了 1 微秒,并且在此期间可能会在某处释放锁。

【问题讨论】:

  • 来自docsSpinWait 封装了纺丝和真正屈服的良好组合。”
  • @GuruStron 我认为这句话与上一句有关,“在单处理器机器上,总是使用产量而不是忙等待”,但是我在 SMP 机器上运行我的代码。跨度>
  • 我对问题进行了重新表述,使其不涉及基于意见的答案。
  • SpinLock 中的基本方法是优化上下文切换,在放弃之前旋转一段时间,让操作系统阻塞线程以允许另一个线程运行。 “一段时间”需要小于上下文切换成本的两倍才能实际优化任何东西。上下文切换需要 3,000 到 15,000 个处理器周期,给予或接受。程序休眠 10 秒,或大约 30,000,000,000 个周期。所以你会期望看到 100% * 2 * 15,000 / 30,000,000,000 = 0.0001% cpu 负载。是的,看起来像 0。

标签: c# multithreading spinlock


【解决方案1】:

关于您编辑的问题,是的,忙碌的旋转将为您提供线程之间的最低通信延迟,但您将通过消耗 CPU 时间来为此付出代价。

另一方面,您可以使用不那么激进的SpinWait,或者您可以在两者之间编写一些代码,例如this

这归结为您对延迟的重视程度和您必须保留多少内核之间的权衡。

【讨论】:

    猜你喜欢
    • 2015-03-13
    • 2012-04-21
    • 1970-01-01
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 2019-02-19
    • 1970-01-01
    • 2021-06-20
    相关资源
    最近更新 更多