首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C 跨平台高精度計時器實作:升序鏈表、原子鎖、非同步回呼與完整生命週期管理

     編輯:關於C語言

C 跨平台高精度計時器實作:升序鏈表、原子鎖、非同步回呼與完整生命週期管理

計時器是事件驅動系統的心跳。本文呈現一個毫秒級精度、跨平台、執行緒安全的 C 計時器庫 sctimer,採用升序鏈表管理定時事件,支援一次性/週期性、同步/非同步模式,並提供完整建構/銷毀介面。

一、介面設計

// 新增計時器
// start: 延遲啟動(ms),0=立即
// cut: 執行次數,0=永久,1=一次
// intval: 間隔(ms)
// timer: 回呼函式 void(*)(void*)
// arg: 回呼參數
// fb: 0=同步(在計時器執行緒), 1=非同步(建立新執行緒)
// 回傳: 計時器唯一 ID
int st_add(int start, int cut, int intval, vdel_f timer, void *arg, bool fb);

// 刪除計時器
inline void st_del(int timer_id);

使用範例

// 1 秒後同步執行一次
st_add(1000, 1, 0, callback, arg, false);

// 立即啟動,每 200ms 非同步執行,永久
st_add(0, 0, 200, callback, arg, true);

二、核心資料結構

// 定時器節點
struct stnode {
    int id;             // 唯一 ID
    time_t stime;       // 觸發秒數
    int ms;             | 毫秒餘數
    int cut;            // 剩餘執行次數 (-1=永久)
    int intval;         // 間隔
    int type;           // 0=同步, 1=非同步
    vdel_f timer;       // 回呼
    void *arg;          // 參數
    struct stnode *next;
};

// 管理器(單例)
struct stlist {
    int lock;           // 原子鎖
    int nowid;          // ID 分配器
    int status;         // 0=停止, 1=運行
    pthread_t tid;      // 主迴圈執行緒
    struct stnode *head; // 升序鏈表頭
} _st;

三、升序鏈表優勢

  • 頭節點永遠是最快到期的計時器
  • 插入 O(N),但執行檢查 O(1)
  • 避免最小堆頻繁交換,適合「大量相同時刻事件」場景

四、關鍵演算法

1. 節點插入 _stlist_add(保持升序)

static bool _stlist_add(struct stlist *st, struct stnode *node) {
    if (!st->head || _stnode_cmp(node, st->head)) {
        node->next = st->head;
        st->head = node;
        return true;  // 新頭節點,需喚醒主迴圈
    }
    struct stnode *cur = st->head;
    while (cur->next && !_stnode_cmp(node, cur->next))
        cur = cur->next;
    node->next = cur->next;
    cur->next = node;
    return false;
}

2. 主迴圈 _stlist_loop

static void *_stlist_loop(struct stlist *st) {
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    pthread_detach(pthread_self());
    while (st->head) {
        pthread_testcancel();
        int wait_ms = _sleeptime(st);  // 計算至頭節點觸發的毫秒數
        if (wait_ms <= 0 || st->head->cut == 1)
            _slnode_again_run(st);     // 觸發執行並重新排程
        else
            SLEEPMS(wait_ms);          // 精確睡眠
    }
    st->status = 0;
    return NULL;
}

3. 觸發與重新排程 _slnode_again_run

  • 取出頭節點
  • cut == 1:釋放節點(一次性任務結束)
  • cut > 1:cut--,更新觸發時間,重新插入鏈表
  • cut == 0(永久):更新時間,重新插入
  • 同步模式直接呼叫回呼;非同步模式建立新執行緒執行

五、跨平台基礎設施 schead.h

  • 睡眠:Linux usleep(ms*1000) / Windows Sleep(ms)
  • 時間:Linux gettimeofday / Windows 以 GetLocalTime 實作
  • 原子鎖:
    • GCC:__sync_lock_test_and_set / __sync_lock_release + usleep 自旋
    • MSVC:InterlockedExchange / InterlockedExchangeAdd + Sleep(0) 讓權
  • 工具巨集:錯誤處理 CERR/CERR_EXIT、陣列長度 LEN、浮點比較 EQ_FLOAT、除錯碼 DEBUG_CODE、暫停 INIT_PAUSE

六、原子鎖實作 scatom.h

// GCC 版本
#define ATOM_LOCK(v) \
    while(ATOM_SET(v, 1)) usleep(2)
#define ATOM_UNLOCK(v) \
    __sync_lock_release(&(v))

// MSVC 版本
#define ATOM_LOCK(v) \
    while(ATOM_SET(v, 1)) Sleep(0)
#define ATOM_UNLOCK(v) \
    ATOM_SET(v, 0)

七、測試驗證

int main() {
    st_add(0, 5, 2000, timer, (void*)1, false);   // 同步,5次,2秒間隔
    st_add(3000, 2, 2000, timer, (void*)2, false); // 延遲3秒啟動
    st_add(4000, 1, 2000, timer, (void*)3, false); // 一次性
    int tid = st_add(0, 0, 1000, timer, (void*)4, true); // 非同步永久
    SLEEPMS(5000);
    st_del(tid);  // 取消永久計時器
    st_add(100, 0, 5000, timer, (void*)5, false);
    sh_pause();   // 除錯模式暫停
    return 0;
}

Linux/Windows 雙平台編譯通過,輸出顯示計時器精確觸發、非同步執行緒正常建構/銷毀。

八、專案結構

  • schead.h/c:跨平台基礎設施
  • scatom.h:原子鎖
  • sctimer.h/c:計時器核心
  • test_sctimer.c:測試程式
  • Makefile:建構腳本

九、擴充方向

  1. 高效能場景:改用時間輪或最小堆將插入優化至 O(log N)
  2. 整合 epoll/kqueue/IOCP 實現單執行緒事件迴圈
  3. 支援高解析度計時器(timerfd/CreateTimerQueueTimer)
  4. 增加計時器群組、暫停/繼續、動態修改間隔功能

本實作展示了系統級計時器的核心設計:有序資料結構、精確睡眠喚醒、同步/非同步靈活切換、跨平台抽象、原子操作保護共享狀態。程式碼結構清晰,適合嵌入式、伺服器、遊戲引擎等場景直接整合或二次開發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved