SDL3_API分类参考_原子操作(CategoryAtomic)

2026-3-6 / 0 评论 / 4 阅读

原子操作子系统(CategoryAtomic)

原子操作相关功能。

重要提示:如果你并非无锁并发编程领域的专家,请勿使用本模块中的任何函数。你应当使用完整的互斥锁来保护数据结构,而非直接使用原子操作。

郑重提醒:此处暗藏风险!

你可以通过以下资源了解无锁编程及其可能引发的微妙问题:
https://learn.microsoft.com/en-us/windows/win32/dxtecharts/lockless-programming

此外,这些资源也包含大量优质内容:

这些原子操作不一定都通过处理器专属的原子指令实现:

  • 条件允许时,会直接使用处理器原生原子指令;
  • 条件不允许时,会基于底层原子操作实现的锁来模拟原子行为。

所有修改内存的原子操作均为全内存屏障(full memory barriers),可确保内存操作的顺序性和可见性。


函数

  • SDL_AddAtomicInt:原子化地为 SDL_AtomicInt 类型变量执行加法操作(返回操作前的原值)
  • SDL_AddAtomicU32:原子化地为 SDL_AtomicU32 类型变量执行加法操作(返回操作前的原值)
  • SDL_CompareAndSwapAtomicInt:对原子整型执行比较并交换(CAS)操作(值匹配时替换,返回操作前的值)
  • SDL_CompareAndSwapAtomicPointer:对原子指针执行比较并交换(CAS)操作(指针匹配时替换,返回操作前的指针)
  • SDL_CompareAndSwapAtomicU32:对原子32位无符号整型执行比较并交换(CAS)操作(值匹配时替换,返回操作前的值)
  • SDL_GetAtomicInt:原子化地读取 SDL_AtomicInt 类型变量的值(确保读取到最新值)
  • SDL_GetAtomicPointer:原子化地读取原子指针的值(确保读取到最新指针)
  • SDL_GetAtomicU32:原子化地读取 SDL_AtomicU32 类型变量的值(确保读取到最新值)
  • SDL_LockSpinlock:自旋锁加锁(忙等方式,不放弃CPU,适合短时间锁定场景)
  • SDL_MemoryBarrierAcquireFunction:执行获取(Acquire)内存屏障(确保后续内存操作可见前面的写入)
  • SDL_MemoryBarrierReleaseFunction:执行释放(Release)内存屏障(确保前面的内存操作对其他线程可见)
  • SDL_SetAtomicInt:原子化地设置 SDL_AtomicInt 类型变量的值(确保写入立即对其他线程可见)
  • SDL_SetAtomicPointer:原子化地设置原子指针的值(确保指针写入立即对其他线程可见)
  • SDL_SetAtomicU32:原子化地设置 SDL_AtomicU32 类型变量的值(确保写入立即对其他线程可见)
  • SDL_TryLockSpinlock:尝试获取自旋锁(非阻塞,获取失败直接返回,不忙等)
  • SDL_UnlockSpinlock:自旋锁解锁(释放自旋锁,允许其他线程获取)

数据类型

  • SDL_SpinLock:自旋锁类型(基于原子操作实现的忙等锁,无内核态切换开销)

结构体

  • SDL_AtomicInt:原子整型结构体(封装int类型,支持原子操作)
  • SDL_AtomicU32:原子32位无符号整型结构体(封装uint32_t类型,支持原子操作)

枚举

  • (无)

  • SDL_AtomicDecRef:原子化地减少引用计数(基于SDL_AtomicInt实现,返回操作后的值)
  • SDL_AtomicIncRef:原子化地增加引用计数(基于SDL_AtomicInt实现,返回操作后的值)
  • SDL_CompilerBarrier:编译器屏障(阻止编译器重排指令,不影响CPU执行顺序)
  • SDL_CPUPauseInstruction:CPU暂停指令(自旋锁中使用,减少忙等时的CPU占用)
  • SDL_MemoryBarrierAcquire:获取(Acquire)内存屏障宏(封装SDL_MemoryBarrierAcquireFunction)
  • SDL_MemoryBarrierRelease:释放(Release)内存屏障宏(封装SDL_MemoryBarrierReleaseFunction)

FreeBASIC 示例代码

' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库)
#Include "SDL.bi"

' 补充原子操作结构体的定义(FreeBASIC 绑定可能缺失)
Type SDL_AtomicInt
    value As Integer
End Type

Type SDL_AtomicU32
    value As UInteger
End Type

' 补充原子操作函数声明(FreeBASIC 绑定可能缺失)
Declare Function SDL_AddAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr, ByVal v As Integer) As Integer
Declare Function SDL_AddAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr, ByVal v As UInteger) As UInteger
Declare Function SDL_CompareAndSwapAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr, ByVal oldval As Integer, ByVal newval As Integer) As Integer
Declare Function SDL_CompareAndSwapAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr, ByVal oldval As UInteger, ByVal newval As UInteger) As UInteger
Declare Function SDL_GetAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr) As Integer
Declare Function SDL_GetAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr) As UInteger
Declare Sub SDL_SetAtomicInt CDecl (ByVal a As SDL_AtomicInt Ptr, ByVal v As Integer)
Declare Sub SDL_SetAtomicU32 CDecl (ByVal a As SDL_AtomicU32 Ptr, ByVal v As UInteger)
Declare Sub SDL_LockSpinlock CDecl (ByVal lock As SDL_SpinLock Ptr)
Declare Function SDL_TryLockSpinlock CDecl (ByVal lock As SDL_SpinLock Ptr) As Integer
Declare Sub SDL_UnlockSpinlock CDecl (ByVal lock As SDL_SpinLock Ptr)

' 原子变量定义
Dim Shared As SDL_AtomicInt g_atomicCounter  ' 原子整型计数器
Dim Shared As SDL_AtomicU32 g_atomicU32Value ' 原子32位无符号整型
Dim Shared As SDL_SpinLock g_spinLock = 0    ' 自旋锁(初始值0为未锁定)
Dim Shared As Any Ptr g_atomicPointer = NULL  ' 原子指针(示例)
Dim Shared As Boolean g_quitThreads = False   ' 线程退出标志

' 线程1:原子加法操作
Function AtomicAddThread CDecl (ByVal data As Any Ptr) As Integer
    Dim As ZString Ptr threadName = Cast(ZString Ptr, data)
    Dim As Integer threadId = SDL_GetCurrentThreadID()

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,执行原子加法操作", threadName, threadId)

    While (Not g_quitThreads)
        ' 原子化地给计数器加1(返回操作前的值)
        Dim As Integer oldValue = SDL_AddAtomicInt(@g_atomicCounter, 1)
        Dim As Integer currValue = SDL_GetAtomicInt(@g_atomicCounter)

        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 原子加法:旧值=%d,新值=%d", threadName, oldValue, currValue)

        ' 原子化地给无符号整型加5
        Dim As UInteger oldU32 = SDL_AddAtomicU32(@g_atomicU32Value, 5)
        Dim As UInteger currU32 = SDL_GetAtomicU32(@g_atomicU32Value)
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 原子U32加法:旧值=%u,新值=%u", threadName, oldU32, currU32)

        ' 模拟工作
        SDL_Delay(300)
    Wend

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId)
    Return 0
End Function

' 线程2:CAS(比较并交换)操作
Function AtomicCASThread CDecl (ByVal data As Any Ptr) As Integer
    Dim As ZString Ptr threadName = Cast(ZString Ptr, data)
    Dim As Integer threadId = SDL_GetCurrentThreadID()

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,执行CAS操作", threadName, threadId)

    While (Not g_quitThreads)
        ' CAS操作:只有当计数器值等于10时,才将其设置为0
        Dim As Integer expected = 10
        Dim As Integer desired = 0
        Dim As Integer oldValue = SDL_CompareAndSwapAtomicInt(@g_atomicCounter, expected, desired)

        If (oldValue = expected) Then
            SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - CAS成功:计数器从%d重置为%d", threadName, expected, desired)
        Else
            SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - CAS失败:期望值=%d,实际值=%d", threadName, expected, oldValue)
        End If

        ' 模拟工作
        SDL_Delay(800)
    Wend

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId)
    Return 0
End Function

' 线程3:自旋锁操作
Function SpinlockThread CDecl (ByVal data As Any Ptr) As Integer
    Dim As ZString Ptr threadName = Cast(ZString Ptr, data)
    Dim As Integer threadId = SDL_GetCurrentThreadID()

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,使用自旋锁", threadName, threadId)

    While (Not g_quitThreads)
        ' 获取自旋锁(忙等,适合短时间锁定)
        SDL_LockSpinlock(@g_spinLock)

        ' 临界区(自旋锁保护,独占访问)
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 获取自旋锁,执行临界区操作", threadName)
        SDL_Delay(100) ' 短时间操作(自旋锁适合短临界区)

        ' 释放自旋锁
        SDL_UnlockSpinlock(@g_spinLock)
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 释放自旋锁", threadName)

        ' 模拟工作
        SDL_Delay(500)
    Wend

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId)
    Return 0
End Function

' 线程4:引用计数原子操作
Function RefCountThread CDecl (ByVal data As Any Ptr) As Integer
    Dim As ZString Ptr threadName = Cast(ZString Ptr, data)
    Dim As Integer threadId = SDL_GetCurrentThreadID()
    Static As SDL_AtomicInt refCount ' 静态引用计数(原子整型)

    ' 初始化引用计数
    If (SDL_GetAtomicInt(@refCount) = 0) Then
        SDL_SetAtomicInt(@refCount, 1)
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 初始化引用计数为1", threadName)
    End If

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 启动,操作引用计数", threadName, threadId)

    ' 增加引用计数(使用宏)
    Dim As Integer newRef = SDL_AtomicIncRef(@refCount)
    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 增加引用计数:%d", threadName, newRef)

    ' 模拟使用资源
    SDL_Delay(1000)

    ' 减少引用计数(使用宏)
    newRef = SDL_AtomicDecRef(@refCount)
    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 减少引用计数:%d", threadName, newRef)

    ' 引用计数为0时释放资源
    If (newRef = 0) Then
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 引用计数为0,释放资源", threadName)
    End If

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 退出", threadName, threadId)
    Return 0
End Function

' 主程序
Sub Main()
    ' 初始化 SDL
    If (SDL_Init(SDL_INIT_THREADS) < 0) Then
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError())
        Exit Sub
    End If

    ' 初始化原子变量
    SDL_SetAtomicInt(@g_atomicCounter, 0)
    SDL_SetAtomicU32(@g_atomicU32Value, 0)

    ' 创建线程
    Dim As String t1Name = "原子加法线程"
    Dim As String t2Name = "CAS操作线程"
    Dim As String t3Name = "自旋锁线程"
    Dim As String t4Name = "引用计数线程1"
    Dim As String t5Name = "引用计数线程2"

    Dim As SDL_Thread Ptr t1 = SDL_CreateThread(@AtomicAddThread, t1Name, StrPtr(t1Name))
    Dim As SDL_Thread Ptr t2 = SDL_CreateThread(@AtomicCASThread, t2Name, StrPtr(t2Name))
    Dim As SDL_Thread Ptr t3 = SDL_CreateThread(@SpinlockThread, t3Name, StrPtr(t3Name))
    Dim As SDL_Thread Ptr t4 = SDL_CreateThread(@RefCountThread, t4Name, StrPtr(t4Name))
    Dim As SDL_Thread Ptr t5 = SDL_CreateThread(@RefCountThread, t5Name, StrPtr(t5Name))

    If (t1 = NULL Or t2 = NULL Or t3 = NULL Or t4 = NULL Or t5 = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建线程失败:%s", SDL_GetError())
        g_quitThreads = True
        SDL_Quit()
        Exit Sub
    End If

    ' 主线程运行 6 秒
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程运行 6 秒...")
    SDL_Delay(6000)

    ' 通知线程退出
    g_quitThreads = True
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程:通知所有线程退出")

    ' 等待所有线程结束
    Dim As Integer result
    SDL_WaitThread(t1, @result)
    SDL_WaitThread(t2, @result)
    SDL_WaitThread(t3, @result)
    SDL_WaitThread(t4, @result)
    SDL_WaitThread(t5, @result)

    ' 打印最终原子变量值
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "最终原子计数器值:%d", SDL_GetAtomicInt(@g_atomicCounter))
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "最终原子U32值:%u", SDL_GetAtomicU32(@g_atomicU32Value))

    ' 退出 SDL
    SDL_Quit()

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出")
End Sub

' 补充引用计数宏的实现(FreeBASIC 绑定可能缺失)
#Define SDL_AtomicIncRef(a) (SDL_AddAtomicInt(a, 1) + 1)
#Define SDL_AtomicDecRef(a) (SDL_AddAtomicInt(a, -1) - 1)

' 运行主程序
Main()

核心知识点补充

  1. 原子操作核心特性

    • 不可分割性:原子操作的执行过程无法被其他线程中断,要么完全执行,要么完全不执行;
    • 内存可见性:修改原子变量的操作会立即对所有线程可见(全内存屏障);
    • 无锁特性:无需互斥锁即可实现线程安全,避免内核态切换开销,但编程复杂度极高。
  2. CAS 操作(Compare and Swap)

    • 核心逻辑:如果变量值等于期望值,则替换为目标值,返回原值
    • 典型用法:实现无锁数据结构(如无锁队列、栈),需配合循环重试;
    • 注意事项:可能引发 ABA 问题(变量值从 A→B→A,CAS 误判为未修改),需额外处理。
  3. 自旋锁适用场景

    • 临界区执行时间极短(微秒级),且线程数不超过 CPU 核心数;
    • 避免在单核 CPU 或临界区较长的场景使用(会导致其他线程无法执行);
    • SDL_CPUPauseInstruction 可减少自旋锁忙等时的 CPU 占用率。
  4. 内存屏障分类

    • Acquire(获取)屏障:确保屏障后的读操作能看到其他线程的写操作;
    • Release(释放)屏障:确保屏障前的写操作对其他线程可见;
    • 编译器屏障:仅阻止编译器指令重排,不影响 CPU 执行顺序。
  5. 原子操作 vs 互斥锁 特性 原子操作 互斥锁
    开销 极低(用户态,无切换) 较高(可能触发内核态)
    适用场景 简单变量操作(计数、标志) 复杂数据结构/长临界区
    编程复杂度 极高(易出隐蔽 bug) 较低(逻辑清晰)
    阻塞方式 无阻塞(CAS 需循环重试) 阻塞(线程进入等待队列)

总结

  1. 核心优势

    • 提供跨平台的原子操作接口,屏蔽不同处理器的原子指令差异;
    • 支持整型、指针、32位无符号整型的原子操作,覆盖基础无锁编程需求;
    • 自旋锁基于原子操作实现,适合短临界区的高效同步。
  2. 使用建议

    • 非无锁编程专家,优先使用互斥锁而非原子操作;
    • 原子操作仅用于简单变量(计数器、标志位),复杂逻辑仍需互斥锁;
    • 自旋锁仅用于临界区极短的场景,避免滥用;
    • CAS 操作需处理 ABA 问题,必要时使用版本号辅助判断。
  3. 关键接口

    • 基础原子操作:SDL_AddAtomicInt()/SDL_SetAtomicInt()/SDL_GetAtomicInt()
    • CAS 操作:SDL_CompareAndSwapAtomicInt()/SDL_CompareAndSwapAtomicPointer()
    • 自旋锁:SDL_LockSpinlock()/SDL_TryLockSpinlock()/SDL_UnlockSpinlock()
    • 内存屏障:SDL_MemoryBarrierAcquire()/SDL_MemoryBarrierRelease()
    • 引用计数:SDL_AtomicIncRef()/SDL_AtomicDecRef()

评论一下?

OωO
取消