定时器子系统(CategoryTimer)
SDL 提供时间管理相关功能,主要用于处理(通常是)短时长的时间操作。
请注意,这与「日历时间(calendar time)」管理不同——日历时间功能由 CategoryTime 模块提供。
本模块涵盖以下核心能力:
- 测量已流逝的时间(
SDL_GetTicks()、SDL_GetPerformanceCounter()); - 让线程休眠指定时长(
SDL_Delay()、SDL_DelayNS()、SDL_DelayPrecise()); - 在指定时长后触发回调函数(
SDL_AddTimer()等)。
此外,SDL 还提供了实用的宏,用于不同时间单位之间的转换(如 SDL_SECONDS_TO_NS() 等)。
函数
- SDL_AddTimer:添加毫秒级定时器(指定触发间隔和回调函数,定时器线程中执行回调)
- SDL_AddTimerNS:添加纳秒级高精度定时器(以纳秒为单位指定触发间隔)
- SDL_Delay:让当前线程休眠指定毫秒数(基础休眠函数,精度受系统限制)
- SDL_DelayNS:让当前线程休眠指定纳秒数(纳秒级休眠,精度高于 SDL_Delay)
- SDL_DelayPrecise:高精度休眠(尽可能精准地休眠指定毫秒数,减少误差)
- SDL_GetPerformanceCounter:获取高性能计数器的当前值(用于高精度时间测量)
- SDL_GetPerformanceFrequency:获取高性能计数器的频率(每秒计数次数)
- SDL_GetTicks:获取 SDL 初始化后的毫秒数(从 SDL_Init 调用开始计时,溢出后循环)
- SDL_GetTicksNS:获取 SDL 初始化后的纳秒数(高精度版本的 SDL_GetTicks)
- SDL_RemoveTimer:移除指定的定时器(停止定时器触发,释放相关资源)
数据类型
- SDL_NSTimerCallback:纳秒级定时器回调函数类型(定时器触发时执行的函数原型)
- SDL_TimerCallback:毫秒级定时器回调函数类型(返回值为下一次触发间隔,返回0则停止)
- SDL_TimerID:定时器唯一标识符(标识一个创建的定时器,用于移除操作)
结构体
- (无)
枚举
- (无)
宏
- SDL_MS_PER_SECOND:每秒的毫秒数(值为 1000)
- SDL_MS_TO_NS:毫秒转换为纳秒(ms × 1000000)
- SDL_NS_PER_MS:每毫秒的纳秒数(值为 1000000)
- SDL_NS_PER_SECOND:每秒的纳秒数(值为 1000000000)
- SDL_NS_PER_US:每微秒的纳秒数(值为 1000)
- SDL_NS_TO_MS:纳秒转换为毫秒(ns ÷ 1000000)
- SDL_NS_TO_SECONDS:纳秒转换为秒(ns ÷ 1000000000)
- SDL_NS_TO_US:纳秒转换为微秒(ns ÷ 1000)
- SDL_SECONDS_TO_NS:秒转换为纳秒(s × 1000000000)
- SDL_US_PER_SECOND:每秒的微秒数(值为 1000000)
- SDL_US_TO_NS:微秒转换为纳秒(us × 1000)
FreeBASIC 示例代码
' 引入 SDL 相关声明(需确保 FreeBASIC 已链接 SDL3 库)
#Include "SDL.bi"
' 补充时间转换宏的定义(FreeBASIC 绑定可能缺失)
#Define SDL_MS_PER_SECOND 1000
#Define SDL_NS_PER_SECOND 1000000000ULL
#Define SDL_NS_PER_MS 1000000ULL
#Define SDL_NS_PER_US 1000ULL
#Define SDL_US_PER_SECOND 1000000ULL
#Define SDL_MS_TO_NS(ms) ((ULLong)(ms) * SDL_NS_PER_MS)
#Define SDL_NS_TO_MS(ns) ((ULLong)(ns) / SDL_NS_PER_MS)
#Define SDL_NS_TO_SECONDS(ns) ((Double)(ns) / SDL_NS_PER_SECOND)
#Define SDL_NS_TO_US(ns) ((ULLong)(ns) / SDL_NS_PER_US)
#Define SDL_SECONDS_TO_NS(s) ((ULLong)(s) * SDL_NS_PER_SECOND)
#Define SDL_US_TO_NS(us) ((ULLong)(us) * SDL_NS_PER_US)
' 全局变量:定时器控制标志
Dim Shared As Boolean g_quitTimer = False
Dim Shared As SDL_TimerID g_timerId = 0 ' 定时器ID
' 毫秒级定时器回调函数
Function TimerCallback CDecl (ByVal interval As UInteger, ByVal param As Any Ptr) As UInteger
Static As Integer counter = 0
counter += 1
Dim As ZString Ptr timerName = Cast(ZString Ptr, param)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 定时器触发(第%d次),间隔:%dms", timerName, counter, interval)
' 返回下一次触发间隔(返回0则停止定时器)
If (g_quitTimer Or counter >= 5) Then
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 定时器停止", timerName)
Return 0
End If
Return interval ' 保持原间隔继续触发
End Function
' 纳秒级定时器回调函数(FreeBASIC 函数原型适配)
Function NSTimerCallback CDecl (ByVal interval As ULLong, ByVal param As Any Ptr) As ULLong
Static As Integer counter = 0
counter += 1
Dim As ZString Ptr timerName = Cast(ZString Ptr, param)
Dim As Double intervalSec = SDL_NS_TO_SECONDS(interval)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 纳秒定时器触发(第%d次),间隔:%.3fs", timerName, counter, intervalSec)
' 返回下一次触发间隔(返回0则停止)
If (counter >= 3) Then
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "%s - 纳秒定时器停止", timerName)
Return 0
End If
Return interval ' 保持原间隔
End Function
' 测量函数执行耗时(使用高性能计数器)
Sub MeasureFunctionTime()
' 获取高性能计数器频率(每秒计数次数)
Dim As ULLong freq = SDL_GetPerformanceFrequency()
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "高性能计数器频率:%llu Hz", freq)
' 记录开始时间
Dim As ULLong start = SDL_GetPerformanceCounter()
' 模拟耗时操作(循环1000万次)
Dim As Integer i
For i = 1 To 10000000
' 空循环
Next
' 记录结束时间
Dim As ULLong end_ = SDL_GetPerformanceCounter()
' 计算耗时(秒)
Dim As Double elapsedSec = (end_ - start) / (Double)freq
Dim As ULLong elapsedNs = SDL_SECONDS_TO_NS(elapsedSec)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "耗时操作完成:")
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 计数器差值:%llu", end_ - start)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 耗时:%.6f 秒", elapsedSec)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 耗时:%llu 纳秒", elapsedNs)
End Sub
' 主程序
Sub Main()
' 初始化 SDL(必须初始化定时器子系统)
If (SDL_Init(SDL_INIT_TIMER) < 0) Then
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL 初始化失败:%s", SDL_GetError())
Exit Sub
End If
' ========== 1. 基础时间获取 ==========
Dim As UInteger ticks = SDL_GetTicks()
Dim As ULLong ticksNs = SDL_GetTicksNS()
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "SDL 初始化后时间:")
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 毫秒数:%u ms", ticks)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, " - 纳秒数:%llu ns", ticksNs)
' ========== 2. 测量函数耗时 ==========
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "开始测量函数耗时...")
MeasureFunctionTime()
' ========== 3. 线程休眠测试 ==========
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "主线程休眠 1.5 秒(基础 Delay)...")
Dim As UInteger sleepMs = 1500
Dim As ULLong sleepNs = SDL_MS_TO_NS(sleepMs)
Dim As ULLong sleepStart = SDL_GetPerformanceCounter()
SDL_Delay(sleepMs) ' 基础毫秒休眠
Dim As ULLong sleepEnd = SDL_GetPerformanceCounter()
Dim As Double actualSleepSec = (sleepEnd - sleepStart) / (Double)SDL_GetPerformanceFrequency()
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "实际休眠时间:%.3f 秒(目标:%.3f 秒)", actualSleepSec, sleepMs / 1000.0)
' 高精度休眠测试
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "主线程高精度休眠 500ms...")
SDL_DelayPrecise(500)
' ========== 4. 毫秒级定时器 ==========
Dim As String timerName = "毫秒定时器"
g_timerId = SDL_AddTimer(1000, @TimerCallback, StrPtr(timerName)) ' 1秒触发一次
If (g_timerId = 0) Then
SDL_LogError(SDL_LOG_CATEGORY_TIMER, "创建毫秒定时器失败:%s", SDL_GetError())
Else
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "创建毫秒定时器成功(ID:%d)", g_timerId)
End If
' ========== 5. 纳秒级定时器 ==========
Dim As String nsTimerName = "纳秒定时器"
Dim As ULLong nsInterval = SDL_SECONDS_TO_NS(2.5) ' 2.5秒
Dim As SDL_TimerID nsTimerId = SDL_AddTimerNS(nsInterval, @NSTimerCallback, StrPtr(nsTimerName))
If (nsTimerId = 0) Then
SDL_LogError(SDL_LOG_CATEGORY_TIMER, "创建纳秒定时器失败:%s", SDL_GetError())
Else
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "创建纳秒定时器成功(ID:%d,间隔:%.3fs)", nsTimerId, SDL_NS_TO_SECONDS(nsInterval))
End If
' 主线程等待定时器执行完成(7秒)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "主线程等待 7 秒...")
SDL_Delay(7000)
' 手动停止定时器(演示用,实际回调返回0会自动停止)
If (g_timerId <> 0) Then
SDL_RemoveTimer(g_timerId)
SDL_LogInfo(SDL_LOG_CATEGORY_TIMER, "手动移除毫秒定时器(ID:%d)", g_timerId)
End If
' 清理 SDL
SDL_Quit()
SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "程序正常退出")
End Sub
' 补充定时器函数声明(FreeBASIC 绑定可能缺失)
Declare Function SDL_AddTimer CDecl (ByVal interval As UInteger, ByVal callback As SDL_TimerCallback, ByVal param As Any Ptr) As SDL_TimerID
Declare Function SDL_AddTimerNS CDecl (ByVal interval As ULLong, ByVal callback As SDL_NSTimerCallback, ByVal param As Any Ptr) As SDL_TimerID
Declare Function SDL_RemoveTimer CDecl (ByVal id As SDL_TimerID) As Integer
Declare Function SDL_GetPerformanceCounter CDecl () As ULLong
Declare Function SDL_GetPerformanceFrequency CDecl () As ULLong
Declare Function SDL_GetTicksNS CDecl () As ULLong
Declare Sub SDL_DelayPrecise CDecl (ByVal ms As UInteger)
Declare Sub SDL_DelayNS CDecl (ByVal ns As ULLong)
' 运行主程序
Main()
核心知识点补充
-
时间测量精度分级:
- 基础精度(毫秒级):
SDL_GetTicks()—— 适合普通计时,精度约 1-10ms,受系统时钟影响; - 高精度(纳秒级):
SDL_GetPerformanceCounter()—— 基于 CPU 高性能计数器,精度可达纳秒级,适合测量短耗时操作。
- 基础精度(毫秒级):
-
定时器核心规则:
- 定时器回调函数运行在独立的定时器线程中,不可执行阻塞操作(如
SDL_Delay),也不可调用 SDL 图形/事件相关函数; - 毫秒定时器回调返回值为下一次触发间隔(ms),返回 0 则停止定时器;
- 纳秒定时器回调返回值为下一次触发间隔(ns),返回 0 则停止;
- 需通过
SDL_RemoveTimer()手动移除未自动停止的定时器,避免资源泄漏。
- 定时器回调函数运行在独立的定时器线程中,不可执行阻塞操作(如
-
休眠函数差异: 函数 时间单位 精度 适用场景 SDL_Delay 毫秒 较低(~1ms) 普通休眠,对精度无要求 SDL_DelayNS 纳秒 中等 纳秒级休眠需求 SDL_DelayPrecise 毫秒 较高 需精准控制休眠时长 -
时间单位转换注意事项:
- 所有转换宏使用无符号长整型(ULLong)避免溢出;
- 纳秒转秒/毫秒时建议用浮点型计算,避免整数截断丢失精度;
- 高性能计数器计算耗时公式:
耗时(秒) = (结束值 - 开始值) / 计数器频率。
总结
-
核心优势:
- 提供从毫秒到纳秒的多精度时间管理能力,满足不同场景需求;
- 高性能计数器可实现纳秒级耗时测量,适合性能分析;
- 跨平台统一接口,屏蔽不同系统的定时器/休眠实现差异;
- 内置时间单位转换宏,简化单位换算逻辑。
-
使用建议:
- 初始化 SDL 时必须指定
SDL_INIT_TIMER子系统,否则定时器函数会失败; - 普通计时用
SDL_GetTicks(),高精度计时用SDL_GetPerformanceCounter(); - 定时器回调函数需保持轻量,避免阻塞;
- 休眠时长较短(<10ms)且要求精准时,优先使用
SDL_DelayPrecise()。
- 初始化 SDL 时必须指定
-
关键接口:
- 时间测量:
SDL_GetTicks()/SDL_GetPerformanceCounter()/SDL_GetPerformanceFrequency(); - 线程休眠:
SDL_Delay()/SDL_DelayPrecise()/SDL_DelayNS(); - 定时器:
SDL_AddTimer()/SDL_AddTimerNS()/SDL_RemoveTimer(); - 单位转换:
SDL_MS_TO_NS()/SDL_NS_TO_SECONDS()/SDL_SECONDS_TO_NS()。
- 时间测量:
评论一下?