SDL3_API分类参考_互斥锁(CategoryMutex)

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

互斥锁子系统(CategoryMutex)

SDL 提供了多种线程同步原语,用于保障多线程程序的线程安全。本文档无法全面覆盖线程安全这一复杂主题,但理解这些同步原语的用途、价值和正确用法,是编写正确且安全的多线程程序的关键。

SDL 提供的核心同步原语包括:

  • 互斥锁(Mutexes)SDL_CreateMutex() —— 独占式资源访问控制
  • 读写锁(Read/Write locks)SDL_CreateRWLock() —— 读共享、写独占的资源访问控制
  • 信号量(Semaphores)SDL_CreateSemaphore() —— 控制并发访问数量的同步机制
  • 条件变量(Condition variables)SDL_CreateCondition() —— 线程间的条件等待/通知机制

此外,SDL 还提供了 SDL_InitState 数据类型,用于确保当多个线程同时尝试首次使用某个资源时,只有一个线程负责该资源的初始化/反初始化操作。


函数

  • SDL_BroadcastCondition:广播唤醒所有等待该条件变量的线程
  • SDL_CreateCondition:创建条件变量(用于线程间条件等待/通知)
  • SDL_CreateMutex:创建互斥锁(基础的独占式同步锁)
  • SDL_CreateRWLock:创建读写锁(读共享、写独占的高级锁)
  • SDL_CreateSemaphore:创建信号量(指定初始计数值)
  • SDL_DestroyCondition:销毁条件变量(释放关联资源)
  • SDL_DestroyMutex:销毁互斥锁(释放关联资源)
  • SDL_DestroyRWLock:销毁读写锁(释放关联资源)
  • SDL_DestroySemaphore:销毁信号量(释放关联资源)
  • SDL_GetSemaphoreValue:获取信号量的当前计数值
  • SDL_LockMutex:加锁互斥锁(阻塞式,获取不到锁则等待)
  • SDL_LockRWLockForReading:为读操作加读写锁(共享锁,允许多线程同时读)
  • SDL_LockRWLockForWriting:为写操作加读写锁(独占锁,仅允许一个线程写)
  • SDL_SetInitialized:设置资源的初始化状态(标记为已初始化/已反初始化)
  • SDL_ShouldInit:检查资源是否需要初始化(防止多线程重复初始化)
  • SDL_ShouldQuit:检查是否需要退出(线程退出条件检测)
  • SDL_SignalCondition:唤醒一个等待该条件变量的线程(随机唤醒一个)
  • SDL_SignalSemaphore:信号量计数加1(释放一个资源许可)
  • SDL_TryLockMutex:尝试加锁互斥锁(非阻塞式,获取不到锁直接返回失败)
  • SDL_TryLockRWLockForReading:尝试为读操作加读写锁(非阻塞式)
  • SDL_TryLockRWLockForWriting:尝试为写操作加读写锁(非阻塞式)
  • SDL_TryWaitSemaphore:尝试等待信号量(非阻塞式,计数不足则直接返回失败)
  • SDL_UnlockMutex:解锁互斥锁(释放锁,允许其他线程获取)
  • SDL_UnlockRWLock:解锁读写锁(释放读/写锁)
  • SDL_WaitCondition:等待条件变量(需配合互斥锁使用,阻塞直到被唤醒)
  • SDL_WaitConditionTimeout:带超时的条件变量等待(超时后自动返回)
  • SDL_WaitSemaphore:等待信号量(阻塞式,计数减1,计数为0则等待)
  • SDL_WaitSemaphoreTimeout:带超时的信号量等待(超时后自动返回)

数据类型

  • SDL_Condition:条件变量句柄类型(标识一个创建的条件变量)
  • SDL_Mutex:互斥锁句柄类型(标识一个创建的互斥锁)
  • SDL_RWLock:读写锁句柄类型(标识一个创建的读写锁)
  • SDL_Semaphore:信号量句柄类型(标识一个创建的信号量)

结构体

  • SDL_InitState:资源初始化状态结构体(用于多线程安全的资源初始化控制)

枚举

  • SDL_InitStatus:初始化状态枚举(未初始化/初始化中/已初始化/反初始化中)

  • SDL_ACQUIRE:标记获取独占锁的函数(线程安全分析用)
  • SDL_ACQUIRE_SHARED:标记获取共享锁的函数(线程安全分析用)
  • SDL_ACQUIRED_AFTER:标记锁的获取顺序(当前函数获取的锁在指定锁之后)
  • SDL_ACQUIRED_BEFORE:标记锁的获取顺序(当前函数获取的锁在指定锁之前)
  • SDL_ASSERT_CAPABILITY:断言当前线程持有指定的独占锁(调试用)
  • SDL_ASSERT_SHARED_CAPABILITY:断言当前线程持有指定的共享锁(调试用)
  • SDL_CAPABILITY:定义线程安全能力(锁)的类型别名(线程安全分析用)
  • SDL_EXCLUDES:标记变量/函数排除指定锁(线程安全分析用)
  • SDL_GUARDED_BY:标记变量受指定独占锁保护(线程安全分析用)
  • SDL_NO_THREAD_SAFETY_ANALYSIS:禁用当前函数的线程安全分析
  • SDL_PT_GUARDED_BY:标记指针指向的变量受指定独占锁保护(线程安全分析用)
  • SDL_RELEASE:标记释放独占锁的函数(线程安全分析用)
  • SDL_RELEASE_GENERIC:标记释放通用锁的函数(线程安全分析用)
  • SDL_RELEASE_SHARED:标记释放共享锁的函数(线程安全分析用)
  • SDL_REQUIRES:标记函数要求调用线程持有指定独占锁(线程安全分析用)
  • SDL_REQUIRES_SHARED:标记函数要求调用线程持有指定共享锁(线程安全分析用)
  • SDL_RETURN_CAPABILITY:标记函数返回一个锁(线程安全分析用)
  • SDL_SCOPED_CAPABILITY:标记作用域锁(自动释放锁,线程安全分析用)
  • SDL_THREAD_ANNOTATION_ATTRIBUTE__:线程注解属性的基础宏(内部使用)
  • SDL_TRY_ACQUIRE:标记尝试获取独占锁的函数(线程安全分析用)
  • SDL_TRY_ACQUIRE_SHARED:标记尝试获取共享锁的函数(线程安全分析用)

FreeBASIC 示例代码

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

' 全局共享资源
Dim Shared As Integer g_sharedCounter = 0
' 同步原语
Dim Shared As SDL_Mutex Ptr g_mutex = NULL               ' 互斥锁
Dim Shared As SDL_RWLock Ptr g_rwLock = NULL             ' 读写锁
Dim Shared As SDL_Semaphore Ptr g_semaphore = NULL       ' 信号量
Dim Shared As SDL_Condition Ptr g_condition = NULL       ' 条件变量
Dim Shared As SDL_Mutex Ptr g_condMutex = NULL           ' 条件变量配套互斥锁
Dim Shared As SDL_InitState g_initState                  ' 初始化状态
' 线程控制标志
Dim Shared As Boolean g_quitThreads = False

' 初始化状态的辅助函数声明(FreeBASIC 绑定可能缺失)
Declare Function SDL_ShouldInit CDecl (ByVal state As SDL_InitState Ptr) As Integer
Declare Sub SDL_SetInitialized CDecl (ByVal state As SDL_InitState Ptr, ByVal status As SDL_InitStatus)

' 线程1:使用互斥锁访问共享计数器
Function MutexThread 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_LockMutex(g_mutex)

        ' 安全访问共享资源
        g_sharedCounter += 1
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 互斥锁:计数器 = %d", threadName, g_sharedCounter)

        ' 解锁互斥锁
        SDL_UnlockMutex(g_mutex)

        ' 模拟工作
        SDL_Delay(300)
    Wend

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

' 线程2:使用读写锁读取共享计数器(读线程)
Function ReadRWLockThread 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_LockRWLockForReading(g_rwLock)

        ' 安全读取共享资源
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 读锁:计数器 = %d", threadName, g_sharedCounter)

        ' 解锁读写锁
        SDL_UnlockRWLock(g_rwLock)

        ' 模拟工作
        SDL_Delay(500)
    Wend

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

' 线程3:使用读写锁修改共享计数器(写线程)
Function WriteRWLockThread 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_LockRWLockForWriting(g_rwLock)

        ' 安全修改共享资源
        g_sharedCounter *= 2
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 写锁:计数器 ×2 = %d", threadName, g_sharedCounter)

        ' 解锁读写锁
        SDL_UnlockRWLock(g_rwLock)

        ' 模拟工作
        SDL_Delay(2000)
    Wend

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

' 线程4:使用信号量控制并发
Function SemaphoreThread 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)
        ' 等待信号量(获取资源许可,最多2个线程同时执行)
        SDL_WaitSemaphore(g_semaphore)

        ' 临界区(最多2个线程同时进入)
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 信号量:获取许可,当前计数 = %d", threadName, SDL_GetSemaphoreValue(g_semaphore))
        SDL_Delay(1000) ' 模拟耗时操作

        ' 释放信号量(归还资源许可)
        SDL_SignalSemaphore(g_semaphore)
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s - 信号量:释放许可,当前计数 = %d", threadName, SDL_GetSemaphoreValue(g_semaphore))

        ' 模拟工作
        SDL_Delay(500)
    Wend

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

' 线程5:使用条件变量等待通知
Function ConditionThread 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)

    ' 加锁条件变量配套的互斥锁
    SDL_LockMutex(g_condMutex)

    ' 等待条件变量(自动释放锁,被唤醒后重新获取锁)
    SDL_WaitCondition(g_condition, g_condMutex)

    ' 被唤醒后执行
    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "%s (ID: %d) 收到通知,计数器 = %d", threadName, threadId, g_sharedCounter)

    ' 解锁互斥锁
    SDL_UnlockMutex(g_condMutex)

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

' 初始化共享资源(线程安全)
Sub InitSharedResource()
    ' 确保只有一个线程执行初始化
    If (SDL_ShouldInit(@g_initState)) Then
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "主线程:初始化共享资源...")

        ' 初始化同步原语
        g_mutex = SDL_CreateMutex()
        g_rwLock = SDL_CreateRWLock()
        g_semaphore = SDL_CreateSemaphore(2) ' 初始计数2,最多2个线程并发
        g_condition = SDL_CreateCondition()
        g_condMutex = SDL_CreateMutex()

        ' 标记初始化完成
        SDL_SetInitialized(@g_initState, SDL_INIT_STATUS_INITIALIZED)
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "主线程:共享资源初始化完成")
    End If
End Sub

' 主程序
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

    ' 初始化共享资源(线程安全)
    InitSharedResource()

    ' 检查初始化是否成功
    If (g_mutex = NULL Or g_rwLock = NULL Or g_semaphore = NULL Or g_condition = NULL Or g_condMutex = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_THREAD, "同步原语创建失败:%s", SDL_GetError())
        g_quitThreads = True
        SDL_Quit()
        Exit Sub
    End If

    ' 创建线程
    Dim As String t1Name = "互斥锁线程"
    Dim As String t2Name = "读锁线程1"
    Dim As String t3Name = "写锁线程"
    Dim As String t4Name = "信号量线程1"
    Dim As String t5Name = "信号量线程2"
    Dim As String t6Name = "信号量线程3"
    Dim As String t7Name = "条件变量线程"

    Dim As SDL_Thread Ptr t1 = SDL_CreateThread(@MutexThread, t1Name, StrPtr(t1Name))
    Dim As SDL_Thread Ptr t2 = SDL_CreateThread(@ReadRWLockThread, t2Name, StrPtr(t2Name))
    Dim As SDL_Thread Ptr t3 = SDL_CreateThread(@WriteRWLockThread, t3Name, StrPtr(t3Name))
    Dim As SDL_Thread Ptr t4 = SDL_CreateThread(@SemaphoreThread, t4Name, StrPtr(t4Name))
    Dim As SDL_Thread Ptr t5 = SDL_CreateThread(@SemaphoreThread, t5Name, StrPtr(t5Name))
    Dim As SDL_Thread Ptr t6 = SDL_CreateThread(@SemaphoreThread, t6Name, StrPtr(t6Name))
    Dim As SDL_Thread Ptr t7 = SDL_CreateThread(@ConditionThread, t7Name, StrPtr(t7Name))

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

    ' 主线程运行 5 秒
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程运行 5 秒...")
    SDL_Delay(5000)

    ' 发送条件变量通知
    SDL_LockMutex(g_condMutex)
    SDL_SignalCondition(g_condition) ' 唤醒等待条件变量的线程
    SDL_UnlockMutex(g_condMutex)
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程:发送条件变量通知")

    ' 通知所有线程退出
    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_WaitThread(t6, @result)
    SDL_WaitThread(t7, @result)

    ' 清理资源
    SDL_DestroyMutex(g_mutex)
    SDL_DestroyRWLock(g_rwLock)
    SDL_DestroySemaphore(g_semaphore)
    SDL_DestroyCondition(g_condition)
    SDL_DestroyMutex(g_condMutex)

    ' 退出 SDL
    SDL_Quit()

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出,最终计数器值:%d", g_sharedCounter)
End Sub

' 运行主程序
Main()

核心知识点补充

  1. 同步原语适用场景

    • 互斥锁(Mutex):最简单的独占锁,适用于所有需要独占访问的场景(如简单的计数器修改);
    • 读写锁(RWLock):读多写少的场景(如配置文件读取/更新),读操作共享,写操作独占,提升并发效率;
    • 信号量(Semaphore):控制并发数量的场景(如线程池、资源池),计数值表示可用资源数;
    • 条件变量(Condition):线程间等待/通知的场景(如生产者-消费者模型),必须配合互斥锁使用。
  2. 关键使用规则

    • 互斥锁/读写锁:加锁和解锁必须成对出现,且在同一个线程中执行;
    • 条件变量SDL_WaitCondition() 会自动释放传入的互斥锁,被唤醒后重新获取锁;
    • 信号量SDL_WaitSemaphore() 使计数减1(获取资源),SDL_SignalSemaphore() 使计数加1(释放资源),计数为0时 Wait 会阻塞;
    • SDL_InitStateSDL_ShouldInit() 确保只有一个线程执行初始化,避免多线程重复初始化资源。
  3. 非阻塞操作

    • SDL_TryLockMutex()/SDL_TryLockRWLockForReading()/SDL_TryLockRWLockForWriting():非阻塞加锁,获取不到锁时直接返回失败,适合不想阻塞的场景;
    • SDL_TryWaitSemaphore():非阻塞等待信号量,计数不足时直接返回失败。
  4. 超时操作

    • SDL_WaitConditionTimeout():带超时的条件变量等待,超时时间单位为毫秒;
    • SDL_WaitSemaphoreTimeout():带超时的信号量等待,避免线程永久阻塞。

总结

  1. 核心优势

    • 提供跨平台统一的线程同步接口,屏蔽不同系统的同步原语差异;
    • 涵盖从基础互斥锁到高级条件变量的完整同步方案,满足不同场景需求;
    • SDL_InitState 简化多线程安全的资源初始化逻辑。
  2. 使用建议

    • 优先选择最贴合场景的同步原语(读多写少用读写锁,并发控制用信号量);
    • 加锁/解锁必须成对,避免死锁(如忘记解锁、解锁非当前线程持有的锁);
    • 条件变量必须配合互斥锁使用,且等待前需加锁;
    • 信号量计数值需合理设置,避免资源耗尽或过度并发。
  3. 关键接口

    • 互斥锁:SDL_CreateMutex()/SDL_LockMutex()/SDL_UnlockMutex()
    • 读写锁:SDL_CreateRWLock()/SDL_LockRWLockForReading()/SDL_LockRWLockForWriting()/SDL_UnlockRWLock()
    • 信号量:SDL_CreateSemaphore()/SDL_WaitSemaphore()/SDL_SignalSemaphore()
    • 条件变量:SDL_CreateCondition()/SDL_WaitCondition()/SDL_SignalCondition()/SDL_BroadcastCondition()
    • 初始化控制:SDL_ShouldInit()/SDL_SetInitialized()

评论一下?

OωO
取消