导读:本期聚焦于小伙伴创作的《C# SpinWait.SpinUntil怎么用?自旋等待和阻塞等待有什么区别》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C# SpinWait.SpinUntil怎么用?自旋等待和阻塞等待有什么区别》有用,将其分享出去将是对创作者最好的鼓励。

在C#多线程编程中,等待某个条件满足是高频操作,SpinWait.SpinUntil作为自旋等待的典型实现,和传统的阻塞等待有不同的适用场景,理解二者的差异能帮助开发者写出更高效的并发代码。

C# SpinWait.SpinUntil怎么用?自旋等待和阻塞等待有什么区别

SpinWait.SpinUntil基本用法

SpinWait.SpinUntil是System.Threading命名空间下的静态方法,它会让当前线程进入自旋状态,不断检查传入的条件委托是否返回true,直到条件满足或者超过指定的超时时间。

方法有两个常用重载:

  • SpinUntil(Func<bool> condition):无限期自旋等待条件满足
  • SpinUntil(Func<bool> condition, int millisecondsTimeout):自旋等待指定毫秒数,超时后返回false
  • SpinUntil(Func<bool> condition, TimeSpan timeout):自旋等待指定的时间间隔,超时后返回false

基础使用示例

下面的代码演示了使用SpinWait.SpinUntil等待一个共享变量状态变化的场景:

using System;
using System.Threading;

class Program
{
    // 共享状态变量
    private static bool _isReady = false;

    static void Main()
    {
        // 启动一个后台线程修改共享状态
        Thread workerThread = new Thread(WorkerMethod);
        workerThread.Start();

        Console.WriteLine("主线程开始自旋等待条件满足");
        // 自旋等待_isReady变为true,最多等待2秒
        bool isSuccess = SpinWait.SpinUntil(() => _isReady, 2000);

        if (isSuccess)
        {
            Console.WriteLine("等待成功,条件已满足");
        }
        else
        {
            Console.WriteLine("等待超时,条件未满足");
        }

        workerThread.Join();
    }

    static void WorkerMethod()
    {
        // 模拟业务处理耗时
        Thread.Sleep(1000);
        _isReady = true;
        Console.WriteLine("后台线程已修改共享状态");
    }
}

自旋等待和阻塞等待的核心区别

阻塞等待的典型实现是Thread.SleepMonitor.WaitAutoResetEvent.WaitOne等,二者的核心差异体现在CPU资源占用和适用场景上:

对比维度自旋等待(SpinWait.SpinUntil)阻塞等待
CPU占用自旋期间线程不会让出CPU,持续占用CPU资源线程进入等待队列,会让出CPU资源
上下文切换无线程上下文切换开销存在线程上下文切换的开销
适用场景等待时间极短(通常几微秒到几毫秒)等待时间较长(通常超过几毫秒)
超时处理支持超时参数,超时后返回false大部分阻塞等待方法也支持超时参数

SpinWait.SpinUntil的适用场景

自旋等待的本质是用CPU时间换上下文切换的开销,因此只适合等待时间非常短的场景,比如:

  • 等待一个轻量级的锁释放,预计等待时间小于1毫秒
  • 等待一个内存中的状态位变化,且变化频率很高
  • 在锁竞争非常低的场景下,实现简单的自旋锁逻辑

如果等待时间不确定或者可能较长,使用自旋等待会导致CPU空转,反而降低整体性能,此时应该选择阻塞等待。

注意事项

使用SpinWait.SpinUntil时需要注意以下几点:

  • 不要在自旋的条件委托中做复杂的计算,否则会进一步浪费CPU资源
  • 自旋等待过程中线程不会被中断,也不会响应Thread.Interrupt调用
  • 如果等待的条件依赖其他线程的操作,要确保其他线程能及时修改条件状态,避免无限自旋
  • 在单核CPU场景下,自旋等待的意义不大,因为当前线程自旋时会占用唯一的CPU核心,其他线程无法运行修改条件,可能导致长时间自旋甚至死锁

自旋锁的简单实现示例

下面是用SpinWait.SpinUntil实现的一个简单自旋锁示例:

using System;
using System.Threading;

public class SimpleSpinLock
{
    private int _isLocked = 0;

    public void Enter()
    {
        // 自旋等待锁释放,直到成功获取锁
        SpinWait.SpinUntil(() =>
        {
            // 使用Interlocked.CompareExchange保证原子操作
            return Interlocked.CompareExchange(ref _isLocked, 1, 0) == 0;
        });
    }

    public void Exit()
    {
        Interlocked.Exchange(ref _isLocked, 0);
    }
}

这个自旋锁适合锁持有时间极短的场景,如果锁持有时间较长,替换成Monitor或者Semaphore等阻塞式的同步原语会更合适。

SpinWait.SpinUntilC#自旋等待阻塞等待修改时间:2026-06-11 18:51:18

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。