线程子系统(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()
核心知识点补充
-
线程函数规范:
- SDL 线程函数必须是
CDecl调用约定,返回值为Integer,参数为Any Ptr; - 线程函数的参数可用于传递任意数据(如示例中的线程名称);
- 线程退出时返回的整数值可通过
SDL_WaitThread()获取(退出码)。
- SDL 线程函数必须是
-
线程本地存储(TLS):
- 每个线程通过同一个 TLS 键访问的数据是独立的,无需加锁;
- 设置 TLS 时可指定析构函数(如示例中的
SDL_free),线程退出时自动释放数据; - 使用完 TLS 键后需调用
SDL_CleanupTLS()清理全局资源。
-
线程管理关键操作:
- 等待线程:
SDL_WaitThread()会阻塞当前线程,直到目标线程结束,适合需要等待线程结果的场景; - 分离线程:
SDL_DetachThread()后线程无需等待,结束后自动释放资源,适合无需关注结果的后台线程; - 优先级:
SDL_SetCurrentThreadPriority()仅在当前线程内调用有效,优先级受系统限制(并非所有平台都支持)。
- 等待线程:
-
线程安全:
- 共享资源(如全局变量)必须通过互斥锁(
SDL_Mutex)保护,避免数据竞争; - TLS 数据是线程专属的,访问时无需加锁,适合存储线程私有状态。
- 共享资源(如全局变量)必须通过互斥锁(
总结
-
核心优势:
- 跨平台统一的线程管理接口,屏蔽不同系统的线程 API 差异;
- 支持线程本地存储(TLS),简化线程私有数据的管理;
- 兼容无线程支持的平台,函数调用不会崩溃(仅返回失败)。
-
使用建议:
- 线程函数必须遵循
CDecl调用约定,否则会导致崩溃; - 共享资源必须加锁保护,TLS 数据无需加锁;
- 线程创建后要么用
SDL_WaitThread()等待,要么用SDL_DetachThread()分离,避免资源泄漏; - 退出程序前需确保所有线程正常终止,避免僵尸线程。
- 线程函数必须遵循
-
关键接口:
- 线程创建:
SDL_CreateThread(); - 线程同步:
SDL_WaitThread()/SDL_DetachThread(); - TLS 管理:
SDL_CreateTLS()/SDL_SetTLS()/SDL_GetTLS()/SDL_CleanupTLS(); - 线程信息:
SDL_GetCurrentThreadID()/SDL_GetThreadID()/SDL_GetThreadState()。
- 线程创建:
评论一下?