SDL3_API分类参考_线程(CategoryThread)

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

线程子系统(CategoryThread)

SDL 提供了跨平台的线程管理功能,主要用于线程的创建、优先级设置和终止处理。

此外,SDL 还支持线程本地存储(Thread Local Storage, TLS) —— 即每个线程拥有唯一的专属数据,但可通过同一个键值访问这些数据。

平台兼容性说明

在不支持线程的平台上(例如未启用 pthreads 的 Emscripten 环境),这些函数依然存在,但 SDL_CreateThread() 这类核心函数会直接返回失败,不会执行实际的线程创建操作。

线程安全注意事项

如果要使用线程功能,你必须充分理解线程安全相关的防护措施:线程的锁定和同步机制由 SDL_mutex.h 中的函数提供(可参考 CategoryMutex 文档)。


函数

  • SDL_CleanupTLS:清理指定的线程本地存储(TLS)键,释放关联的资源
  • SDL_CreateThread:创建一个新线程(指定线程函数、线程名称和传递参数)
  • SDL_CreateThreadWithProperties:通过属性配置创建线程(更精细的线程控制)
  • SDL_DetachThread:分离线程(线程结束后自动释放资源,无需等待)
  • SDL_GetCurrentThreadID:获取当前执行线程的唯一标识符(ThreadID)
  • SDL_GetThreadID:获取指定线程对象的唯一标识符(ThreadID)
  • SDL_GetThreadName:获取指定线程的名称(创建线程时设置的名称)
  • SDL_GetThreadState:获取指定线程的当前状态(运行/终止/等待等)
  • SDL_GetTLS:从线程本地存储中,通过 TLS 键获取当前线程的专属数据
  • SDL_SetCurrentThreadPriority:设置当前执行线程的优先级
  • SDL_SetTLS:将指定数据关联到当前线程的 TLS 键(设置线程本地存储数据)
  • SDL_WaitThread:等待指定线程执行完成(阻塞当前线程,直到目标线程结束)

数据类型

  • SDL_Thread:线程对象句柄类型(标识一个创建的线程)
  • SDL_ThreadFunction:线程函数的函数指针类型(线程入口函数的标准格式)
  • SDL_ThreadID:线程唯一标识符类型(用于区分不同线程)
  • SDL_TLSDestructorCallback:TLS 析构函数回调类型(线程退出时清理 TLS 数据)
  • SDL_TLSID:线程本地存储(TLS)的键类型(用于访问线程专属数据)

结构体

  • (无)

枚举

  • SDL_ThreadPriority:线程优先级枚举(低/正常/高/时间关键等)
  • SDL_ThreadState:线程状态枚举(运行中/已终止/等待中/已分离等)

  • (无)

FreeBASIC 示例代码

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

' 全局变量:用于线程间同步的互斥锁
Dim Shared As SDL_Mutex Ptr g_mutex = NULL
' 全局变量:线程退出标志
Dim Shared As Boolean g_quitThread = False
' TLS 键:存储每个线程的专属计数器
Dim Shared As SDL_TLSID g_tlsCounterKey = SDL_TLSID(-1)

' 线程函数:模拟耗时任务
Function ThreadTask CDecl (ByVal data As Any Ptr) As Integer
    ' 获取线程名称(通过传入参数传递)
    Dim As ZString Ptr threadName = Cast(ZString Ptr, data)
    Dim As Integer threadId = SDL_GetCurrentThreadID()

    ' 初始化当前线程的 TLS 计数器(每个线程独立)
    Dim As Integer Ptr counter = Allocate(SizeOf(Integer))
    *counter = 0
    SDL_SetTLS(g_tlsCounterKey, counter, @SDL_free) ' 设置析构函数自动释放

    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程 %s (ID: %d) 启动", threadName, threadId)

    ' 设置当前线程优先级为正常
    SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_NORMAL)

    ' 线程主循环
    While (Not g_quitThread)
        ' 加锁保护共享资源访问
        SDL_LockMutex(g_mutex)

        ' 更新 TLS 计数器(线程专属,无需加锁)
        (*counter) += 1
        SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程 %s 计数器:%d", threadName, *counter)

        ' 解锁
        SDL_UnlockMutex(g_mutex)

        ' 模拟耗时操作
        SDL_Delay(500)
    Wend

    ' 获取并打印最终的 TLS 计数器值
    Dim As Integer Ptr finalCounter = Cast(Integer Ptr, SDL_GetTLS(g_tlsCounterKey))
    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程 %s 结束,最终计数器:%d", threadName, *finalCounter)

    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

    ' 创建互斥锁(线程同步用)
    g_mutex = SDL_CreateMutex()
    If (g_mutex = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建互斥锁失败:%s", SDL_GetError())
        SDL_Quit()
        Exit Sub
    End If

    ' 创建 TLS 键(用于存储线程专属计数器)
    g_tlsCounterKey = SDL_CreateTLS()
    If (g_tlsCounterKey = SDL_TLSID(-1)) Then
        SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建 TLS 键失败:%s", SDL_GetError())
        SDL_DestroyMutex(g_mutex)
        SDL_Quit()
        Exit Sub
    End If

    ' 定义线程名称
    Dim As String thread1Name = "线程1"
    Dim As String thread2Name = "线程2"

    ' 创建两个工作线程
    Dim As SDL_Thread Ptr thread1 = SDL_CreateThread(@ThreadTask, thread1Name, StrPtr(thread1Name))
    Dim As SDL_Thread Ptr thread2 = SDL_CreateThread(@ThreadTask, thread2Name, StrPtr(thread2Name))

    If (thread1 = NULL Or thread2 = NULL) Then
        SDL_LogError(SDL_LOG_CATEGORY_THREAD, "创建线程失败:%s", SDL_GetError())
        g_quitThread = True
        SDL_DestroyMutex(g_mutex)
        SDL_CleanupTLS(g_tlsCounterKey)
        SDL_Quit()
        Exit Sub
    End If

    ' 打印线程信息
    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "主线程 ID:%d", SDL_GetCurrentThreadID())
    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程1 ID:%d", SDL_GetThreadID(thread1))
    SDL_LogInfo(SDL_LOG_CATEGORY_THREAD, "线程2 ID:%d", SDL_GetThreadID(thread2))

    ' 主线程等待 5 秒后通知线程退出
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "主线程等待 5 秒...")
    SDL_Delay(5000)
    g_quitThread = True

    ' 等待线程执行完成
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "等待线程退出...")
    Dim As Integer thread1Result, thread2Result
    SDL_WaitThread(thread1, @thread1Result)
    SDL_WaitThread(thread2, @thread2Result)

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "线程1退出码:%d,线程2退出码:%d", thread1Result, thread2Result)

    ' 清理资源
    SDL_CleanupTLS(g_tlsCounterKey) ' 清理 TLS 键
    SDL_DestroyMutex(g_mutex)       ' 销毁互斥锁
    SDL_Quit()                      ' 退出 SDL

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

' 补充 SDL_CreateTLS 函数声明(FreeBASIC 绑定可能缺失)
Declare Function SDL_CreateTLS CDecl () As SDL_TLSID

' 运行主程序
Main()

核心知识点补充

  1. 线程函数规范

    • SDL 线程函数必须是 CDecl 调用约定,返回值为 Integer,参数为 Any Ptr
    • 线程函数的参数可用于传递任意数据(如示例中的线程名称);
    • 线程退出时返回的整数值可通过 SDL_WaitThread() 获取(退出码)。
  2. 线程本地存储(TLS)

    • 每个线程通过同一个 TLS 键访问的数据是独立的,无需加锁;
    • 设置 TLS 时可指定析构函数(如示例中的 SDL_free),线程退出时自动释放数据;
    • 使用完 TLS 键后需调用 SDL_CleanupTLS() 清理全局资源。
  3. 线程管理关键操作

    • 等待线程SDL_WaitThread() 会阻塞当前线程,直到目标线程结束,适合需要等待线程结果的场景;
    • 分离线程SDL_DetachThread() 后线程无需等待,结束后自动释放资源,适合无需关注结果的后台线程;
    • 优先级SDL_SetCurrentThreadPriority() 仅在当前线程内调用有效,优先级受系统限制(并非所有平台都支持)。
  4. 线程安全

    • 共享资源(如全局变量)必须通过互斥锁(SDL_Mutex)保护,避免数据竞争;
    • TLS 数据是线程专属的,访问时无需加锁,适合存储线程私有状态。

总结

  1. 核心优势

    • 跨平台统一的线程管理接口,屏蔽不同系统的线程 API 差异;
    • 支持线程本地存储(TLS),简化线程私有数据的管理;
    • 兼容无线程支持的平台,函数调用不会崩溃(仅返回失败)。
  2. 使用建议

    • 线程函数必须遵循 CDecl 调用约定,否则会导致崩溃;
    • 共享资源必须加锁保护,TLS 数据无需加锁;
    • 线程创建后要么用 SDL_WaitThread() 等待,要么用 SDL_DetachThread() 分离,避免资源泄漏;
    • 退出程序前需确保所有线程正常终止,避免僵尸线程。
  3. 关键接口

    • 线程创建:SDL_CreateThread()
    • 线程同步:SDL_WaitThread()/SDL_DetachThread()
    • TLS 管理:SDL_CreateTLS()/SDL_SetTLS()/SDL_GetTLS()/SDL_CleanupTLS()
    • 线程信息:SDL_GetCurrentThreadID()/SDL_GetThreadID()/SDL_GetThreadState()

评论一下?

OωO
取消