女人自慰AV免费观看内涵网,日韩国产剧情在线观看网址,神马电影网特片网,最新一级电影欧美,在线观看亚洲欧美日韩,黄色视频在线播放免费观看,ABO涨奶期羡澄,第一导航fulione,美女主播操b

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

定時器作用及實現(xiàn)定時器數(shù)據(jù)結(jié)構(gòu)選取介紹1

jf_78858299 ? 來源:程序員不是碼農(nóng) ? 作者:程序員不是碼農(nóng) ? 2023-04-21 15:20 ? 次閱讀

本文主要介紹定時器作用,實現(xiàn)定時器數(shù)據(jù)結(jié)構(gòu)選取,并詳細介紹了跳表,紅黑樹,時間輪實現(xiàn)定時器的思路和方法。

定時器作用

定時器在各種場景都需要用到,比如游戲的Buff實現(xiàn),Redis中的過期任務(wù),Linux中的定時任務(wù)等等。顧名思義,定時器的主要用途是執(zhí)行定時任務(wù)。

定時器數(shù)據(jù)結(jié)構(gòu)選取

定時器數(shù)據(jù)結(jié)構(gòu)要求:

  • 需要快速找到到期任務(wù),因此,應(yīng)該具有有序性;
  • 其過期執(zhí)行、插入(添加定時任務(wù))和刪除(取消定時任務(wù))的頻率比較高,三種操作效率必須保證

以下為各數(shù)據(jù)結(jié)構(gòu)時間復(fù)雜度表現(xiàn)

有序鏈表:插入O(n),刪除O(1),過期expire執(zhí)行O(1)

最小堆:插入O(logn),刪除O(logn),過期expire執(zhí)行O(1)

紅黑樹:插入O(logn),刪除O(logn),過期expire執(zhí)行O(logn)

哈希表+鏈表(時間輪):插入O(1),刪除O(1),過期expire平均執(zhí)行O(1)(最壞為O(n)

不同開源框架定時器實現(xiàn)方式不一,如,libuv采用最小堆來實現(xiàn),nginx采用紅黑樹實現(xiàn),linux內(nèi)核和skynet采用時間輪算法實現(xiàn)等等。

定時器接口封裝

作為定時器,需要封裝以下4類接口給用戶使用:

  • 創(chuàng)建定時器:init_timer
  • 添加定時任務(wù):add_timer
  • 取消定時任務(wù):cancel_timer
  • 執(zhí)行到期任務(wù):expire_timer

其中執(zhí)行到期任務(wù)有兩種工作方式:

  1. 輪詢: 每隔一個時間片去查找哪些任務(wù)到期
  2. 睡眠/喚醒:不停查找deadline最近任務(wù),到期執(zhí)行,否則sleep;sleep期間,任務(wù)有改變,線程會被喚醒

接下來將介紹分別用跳表、紅黑樹、時間輪來實現(xiàn)定時器。

跳表實現(xiàn)定時器

跳表簡介

跳表是一種動態(tài)的數(shù)據(jù)結(jié)構(gòu),采用空間換時間的思想,在有序鏈表基礎(chǔ)上加入多級索引,通過索引進行二分快速查找,支持快速刪除、插入和查找操作(平均時間復(fù)雜度為O(logN),最壞為O(N)),效率可與平衡樹媲美,實現(xiàn)比其簡單。

下面通過一張圖來簡單說明跳表操作。跳表的最底層即為基本的有序鏈表,存儲所有的數(shù)據(jù),可理解為數(shù)據(jù)層;往上則為索引層,理想狀態(tài)下,上一層為下一層節(jié)點數(shù)的一半。比如,要查找下圖的數(shù)據(jù)為11的節(jié)點,從begin''出發(fā),向右走,如果下一個節(jié)點大于11則往下走,直到找到目標節(jié)點??梢?,跳表要比原始鏈表少比較一些節(jié)點,但前提是需要花更多空間存儲索引節(jié)點。

圖片

image-20210323182236910

跳表實現(xiàn)定時器

  • 跳表查找,插入,刪除(任意節(jié)點、頭節(jié)點)的時間復(fù)雜度大概率趨向于O(logn)
  • 過期任務(wù)查找,只需要跟第一個節(jié)點比較,因其第一個節(jié)點即為最小節(jié)點

學(xué)會吸取開源框架中優(yōu)秀數(shù)據(jù)結(jié)構(gòu)和代碼思想,直接采用redis中跳表結(jié)構(gòu)的實現(xiàn),取出所需部分,用于實現(xiàn)定時器。如下:

跳表數(shù)據(jù)結(jié)構(gòu)

跳表節(jié)點與跳表結(jié)構(gòu)

/*skiplist.h*/
#define ZSKIPLIST_MAXLEVEL 32
#define ZSKIPPLIST 0.25

typedef struct zskiplistNode zskiplistNode;
typedef void (*handler_pt) (zskiplistNode * node);
// 跳表節(jié)點
struct zskiplistNode {
  unsigned long score;  /*用于排序的值*/
  handler_pt handler;  /*處理函數(shù)*/
  struct zskiplistLevel {
    struct zskiplistNode **forward;
  }level[];
};
// 跳表結(jié)構(gòu)
typedef struct zskiplist {
  struct zskiplistNode * header;
  int length;
  int level;  /*跳表層數(shù)*/
}zskiplist;

跳表接口申明

具體接口實現(xiàn)細節(jié)請移步redis源碼。

/*skiplist.h*/
/*創(chuàng)建跳表,初始化*/
zskiplist *zslCreate(void);
/*刪除跳,表釋放資源*/
void zslFree(zskiplist *zsl);
/*插入節(jié)點*/
zskiplistNode *zslInsert(zskiplist *zsl, unsigned long score, handler_pt func);
/*刪除頭節(jié)點*/
void zsklDeleteHead(zskiplist *zsl);
/*刪除任意節(jié)點*/
void zslDelete(zskiplist *zsl, zskplistNode *zn);
/*打印,調(diào)試*/
void zslPrint(zskiplist *zsl);

定時器接口實現(xiàn)

主要介紹四個接口實現(xiàn):初始化定時器,添加定時任務(wù),刪除/取消定時任務(wù),處理定時任務(wù)

// test_user.c  封裝給用戶使用的接口
static uint32_t
current_time() {
 uint32_t t;
    struct timespec ti;
    clock_getttime(CLOCK_MONOTONIC, &ti);
    t = (uint32_t)ti.tv_sec * 1000;
    t += ti.tv_sec / 1000000;
}
zskiplist *init_timer() {
    // 初始化定時器
    return zslCreate();
}
zskiplistNode *add_timer(zskiplist *zsl, uint32_t msec, handler_pt func) {
    // 添加定時任務(wù)
    msec += current_time();
    return zslInsert(zsl, msec, func);
}
void cancel_timer(zskiplist *zsl, zskiplistNode *zn) {
    // 刪除/取消定時任務(wù)
    zslDelete(zsl, zn);
}
void expire_timer(zskiplist *zsl){
    // 處理定時任務(wù)
    zskiplistNode *x;
    uint32_t now = current_time();
    for (;;) {
        x = zslMin(zsl);  // 最近節(jié)點
        if (!x) break;
        if (x->score > now)  break;  // 時間未到
        x->handler(x);  // 執(zhí)行相關(guān)定時任務(wù)
        zslDeleteHead(zsl);  // 執(zhí)行完刪除
    }
}

紅黑樹實現(xiàn)定時器

紅黑樹

紅黑樹是一種自平衡的二叉查找樹,即,插入和刪除操作如果破壞樹的平衡時,需要重新調(diào)整達到平衡狀態(tài)。因此,是一種比較難的數(shù)據(jù)結(jié)構(gòu)。

紅黑樹五條性質(zhì)

  • 每個節(jié)點要么是紅色,要么是黑色
  • 根節(jié)點是黑色
  • 每個葉子結(jié)點是黑色
  • 每個紅節(jié)點的兩個子節(jié)點一定是黑色
  • 任意一節(jié)點到每個葉子節(jié)點的路徑都含相同數(shù)目的黑結(jié)點

弄懂紅黑樹如何調(diào)整樹的平衡,保證滿足這5條性質(zhì),是比較麻煩,需要耐心的去推導(dǎo)一遍,此處不展開。

紅黑樹實現(xiàn)定時器

AVL 樹平衡要求太高,維護平衡操作過多,較復(fù)雜;紅黑樹只需維護一個黑高度,效率較高

紅黑樹查找,刪除,添加時間復(fù)雜度為:O(log(n))

吸取開源框架中優(yōu)秀數(shù)據(jù)結(jié)構(gòu)和代碼思想,選用nginx中的紅黑樹結(jié)構(gòu)

紅黑樹數(shù)據(jù)結(jié)構(gòu)

紅黑樹節(jié)點與紅黑樹

// rbtree.h  紅黑樹數(shù)據(jù)結(jié)構(gòu)以及相關(guān)接口,具體接口實現(xiàn)同上
#ifndef _NGX_RBTREE_H_INCLUDE_
#define _NGX_RBTREE_H_INCLUDE_

typedef unsigned int ngx_rbtree_key_t;
typedef unsigned int ngx_uint_t;
typedef int ngx_rbtree_key_int_t;

// 紅黑樹節(jié)點
typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;
struct ngx_rbtree_node_s {
    ngx_rbtree_key_t key;
    ngx_rbtree_node_t *left;
    ngx_rbtree_node_t *right;
    ngx_rbtree_node_t *parent;
    u_char    color;  // 節(jié)點顏色
    u_char    data;  // 節(jié)點數(shù)據(jù)
};
// 插入函數(shù)指針
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
// 紅黑樹
typedef struct ngx_rbtree_s ngx_rbtree_t;
struct ngx_rbtree_s {
    ngx_rbtree_node_t  *root;
    ngx_rbtree_node_t  *sentinel;
    ngx_rbtree_insert_pt insert;
};

紅黑樹接口聲明

// 紅黑樹初始化
#define ngx_rbtree_init(tree, s, i)       \\
 ngx_rbtree_sentinel_init(s);      \\
 (tree)->root = s;        \\
 (tree)->sentinel = s;       \\
 (tree)->insert = i;        
// 插入操作
void ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
// 刪除操作
void ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
// 插入value
void ngx_rbtree_insert_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node,
                            ngx_rbtree_node_t *sentinel);
// 插入timer
void ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *root,
                                  ngx_rbtree_node_t *node,
                                  ngx_rbtree_node_t *sentinel);
// 獲取下一個節(jié)點
ngx_rbtree_node_t *ngx_rbtree_next(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);
#define ngx_rbt_red(node)    ((node)->color = 1)
#define ngx_rbt_black(node)    ((node)->color = 0)
#define ngx_rbt_is_red(node)   ((node)->color)
#define ngx_rbt_is_black(node)   (!ngx_rbt_is_red(node))
#define ngx_rbt_copy_color(n1, n2)  (n1->color = n2->color)
#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)
// 找到最小值,一直往左走即可
static inline ngx_rbtree_node_t *
ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
    while (node->left != sentinel){
        node = node->left;
    }
    return node;
}

定時器接口實現(xiàn)

// test_user.c  封裝給用戶使用的接口
ngx_rbtree_t     timer;
static ngx_rbtree_node_t   sentinel;
typedef struct timer_entry_s timer_entry_t;
typedef void (*timer_handler_pt)(timer_entry_t *ev);

struct timer_entry_s {
   ngx_rbtree_node_t timer;
    timer_handler_pt  handler;
};
// 初始化
int init_timer() {
    ngx_rbtree_init(&timer, &sentinel, ngx_rbtree_insert_timer_value);
    return 0;
}
// 添加定時任務(wù)
void add_timer(timer_entry_t *te, uint32_t msec) {
    msec += current_time();
    te->timer.key = msec;
    ngx_rbtree_insert(&timer, &te->timer);
}
// 取消定時
void cancel_timer(timer_entry_t *te) {
    ngx_rbtree_delete(&timer, &te->timer);
}
// 執(zhí)行到期任務(wù)
void expire_timer() {
    timer_entry_t *te;
    ngx_rbtree_node_t *sentinel, *root, *node;
    sentinel = timer.sentinel;
    uint32_t now = current_time();
    for(;;){
        root = timer.root;
        if (root == sentinel) break;
        if (node->key > now) break;
        te = (timer_entry_t *) ((char *) node - offsetof(timer_entry_t, timer));
        te->handler(te);
        ngx_rbtree_delete(&timer, &te->timer);
        free(te);
    }
}

以上,為紅黑樹和跳表實現(xiàn)的定時器,多線程環(huán)境下加鎖粒度比較大,高并發(fā)場景下效率不高,而時間輪適合高并發(fā)場景,如下。

時間輪實現(xiàn)定時器

時間輪

可以用于高效的執(zhí)行大量定時任務(wù),如下為分層時間輪示意圖:

圖片

timewheel

時間輪可參考時鐘進行理解,秒針(Seconds wheel)轉(zhuǎn)一圈,則分針(Minutes wheel)走一格,分針(Minutes wheel)轉(zhuǎn)一圈,則時針(Hours wheel)走一格。隨著,時間的流逝,任務(wù)不斷從上層流下下一層,最終到達秒針輪上,當秒針走到時執(zhí)行。

如上所示,時間輪大小為8格,秒針1s轉(zhuǎn)動一格,其每一格所指向的鏈表保存著待執(zhí)行任務(wù)。比如,如果當前指針指向1,要添加一個3s后執(zhí)行的任務(wù),由于1+3=4,即在第4格的鏈表中添加一個任務(wù)節(jié)點即可。如果要添加一個10s后執(zhí)行的任務(wù),10+1=11,超過了秒針輪范圍,因此需要對8取模11 % 8 = 3,即,會把這個任務(wù)放到分針輪上3對應(yīng)的鏈表上,之后再從分針輪把任務(wù)丟到秒針輪上進行處理。也即,**秒針輪(Seconds wheel)**即保存著最近將要執(zhí)行的任務(wù),隨著時間的流逝,任務(wù)會慢慢的從上層流到秒針輪中進行執(zhí)行。

優(yōu)點:加鎖粒度較小,只需要加一個格子即可,一個格子對應(yīng)一串鏈表;適合高并發(fā)場景

缺點:不好刪除

如何解決時間輪定時任務(wù)刪除?

  1. 通過引用計數(shù)來解決
  2. 交由業(yè)務(wù)層處理,將刪除標記設(shè)為true , 在函數(shù)回調(diào)中根據(jù)這個標記判斷是否需要處理

這里介紹兩種定時器實現(xiàn)方案,一種是簡單實現(xiàn)方案,另一種是skynet較為復(fù)雜的實現(xiàn)。

時間輪實現(xiàn)定時器

簡單時間輪實現(xiàn)方案

功能場景:由心跳包進行超時連接檢測,10s未收到則斷開連接

一般做法:map每秒輪詢這個結(jié)構(gòu),檢測所有連接是否超時,收到心跳包,記錄時間戳

缺點:效率很差,每次需要檢測所有連接,時間復(fù)雜度為O(n)

優(yōu)化:分治大法,只需檢測快過期的連接, 采用hash數(shù)組+鏈表形式,數(shù)組大小設(shè)置成16 :[0] + [1] + [2] + ... + [15] ,相同過期時間的放入一個數(shù)組,因此,每次只需檢測最近過期的數(shù)組即可,不需要遍歷所有。

數(shù)據(jù)結(jié)構(gòu)定義

以下為定時器節(jié)點,增加引用計數(shù)ref, 只有當ref為0時刪除連接。

class CTimerNode {
public:
    CTimerNode(int fd) : id(fd), ref(0) {}
    void Offline() {this->ref = 0};
    bool tryKill() {
        if (this->ref == 0) return true;
        DecRef();
        if (this->ref == 0){
            return true;
        }
        return false;
    }
    void IncRef() {this->ref++;}
protected:
    void DecRef() {this->ref--;}
private:
    int ref;
    int id;
}
// 時間輪數(shù)組大小16, (x對16取余)==(x&1111) 落到0-15之間,即落到對應(yīng)的數(shù)組
const int TW_SIZE = 16;
const in EXPIRE = 10; // 過期間隔
const int TW_MASK = TW_SIZE - 1;  // 掩碼, 用于對16取余
static size_t iReadTick = 0;  // 滴答時鐘
typedef list
定時器接口
// 添加定時
void AddTimeOut(TimerWheel &tw, CTimerNode *p) {
    if (p) {
        p->IncRef();
        // 找到iRealTick對應(yīng)數(shù)組的idx(槽位)
        TimeList &le = tw[(iRealTick+EXPIRE) & TW_MASK];
        le.push_back(p);  // 把時間節(jié)點加入list中
    }
}
// 延時調(diào)用
void AddTimeOutDelay(TimeWheel &tw, CTimerNode *p, size_t delay) {
    if (p) {
        p->IncRef();
        TimeList &le = tw[(iRealTick + EXPIRE + delay) & TW_MASK];
        le.push_back(p);
    }
}
// 時間輪移動
void TimerShift(TimeWheel &tw) {
    size_t tick = iRealTick;
    iRealTick++;
    TimeList &le = tw[tick & TW_MASK];
    TimeListIter iter = le.begin();
    for (; iter != le.end(); iter++) {
        CTimerNode *p = *iter;
        if (p && p->trySkill()){
            delete p;
        }
    }
    le.clear();
}
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • Linux
    +關(guān)注

    關(guān)注

    87

    文章

    11459

    瀏覽量

    212788
  • 定時器
    +關(guān)注

    關(guān)注

    23

    文章

    3287

    瀏覽量

    117205
  • 數(shù)據(jù)結(jié)構(gòu)

    關(guān)注

    3

    文章

    573

    瀏覽量

    40600
收藏 人收藏

    評論

    相關(guān)推薦
    熱點推薦

    定時器/計數(shù)基礎(chǔ)

    15-1.實現(xiàn)定時的方法15-2.定時器/計數(shù)結(jié)構(gòu)和工作原理 15-3.
    發(fā)表于 03-23 12:17 ?48次下載

    555定時器

    555定時器555定時器555定時器555定時器555定時器555定時器555
    發(fā)表于 11-10 17:25 ?53次下載

    定時器介紹

    同時用兩個定時器控制蜂鳴器發(fā)聲, 定時器0控制頻率,定時器1控制同個 頻率持續(xù)的時間,間隔2s依次輸出 1,10,50100,2004
    發(fā)表于 02-23 15:56 ?20次下載

    詳細介紹定時器定時器中斷

    在測量控制系統(tǒng)中,常常需要實時時鐘,以實現(xiàn)定時控制、定時測量或定時中斷等。也常需要計數(shù)實現(xiàn)
    的頭像 發(fā)表于 02-09 14:00 ?1.8w次閱讀
    詳細<b class='flag-5'>介紹</b><b class='flag-5'>定時器</b>和<b class='flag-5'>定時器</b>中斷

    STC51定時器定時器中斷

    1.定義定時器介紹: 51單片機的定時器屬于單片機的內(nèi)部資源,其電路的連接和運轉(zhuǎn)均在單片機內(nèi)部完成。2.作用
    發(fā)表于 11-22 14:51 ?5次下載
    STC51<b class='flag-5'>定時器</b>與<b class='flag-5'>定時器</b>中斷

    stm32—定時器配置

    目錄定時器組成通用寄存通用寄存簡介:通用定時器 TIMx (TIM2-TIM5 )的功能:通用定時器
    發(fā)表于 11-22 17:51 ?11次下載
    stm32—<b class='flag-5'>定時器</b>配置

    STM32基于cubeMX實現(xiàn)定時器點燈

    Cortex M3內(nèi)核當中的定時器,它并不屬于芯片廠商的外設(shè),也就是說使用ARM內(nèi)核的不同廠商,都擁有基本結(jié)構(gòu)相同的系統(tǒng)定時器。主要目的是給RTOS提供時鐘節(jié)拍做時間基準?;?b class='flag-5'>定時器
    發(fā)表于 11-23 18:21 ?19次下載
    STM32基于cubeMX<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>定時器</b>點燈

    STM32定時器-基本定時器

    目錄定時器分類基本定時器功能框圖講解基本定時器功能時鐘源計數(shù)時鐘計數(shù)自動重裝載寄存
    發(fā)表于 11-23 18:21 ?32次下載
    STM32<b class='flag-5'>定時器</b>-基本<b class='flag-5'>定時器</b>

    基于硬件定時器的軟件定時器

    出現(xiàn)使用軟件定時器的情況,但是講定時器需要從硬件定時器開始講,軟件定時器是在其基礎(chǔ)之上延伸出來的。硬件定時器
    發(fā)表于 11-25 09:51 ?8次下載
    基于硬件<b class='flag-5'>定時器</b>的軟件<b class='flag-5'>定時器</b>

    STM32——高級定時器、通用定時器、基本定時器的區(qū)別

    STM32——高級定時器、通用定時器、基本定時器的區(qū)別
    發(fā)表于 11-26 15:21 ?110次下載
    STM32——高級<b class='flag-5'>定時器</b>、通用<b class='flag-5'>定時器</b>、基本<b class='flag-5'>定時器</b>的區(qū)別

    SysTick 定時器

    的SysTick定時器實現(xiàn)延時,可以不占用系統(tǒng)定時器,節(jié)約資源。由于SysTick是在CPU核內(nèi)部實現(xiàn)的,跟MCU外設(shè)無關(guān),因此它的代碼可以在不同廠家之間移植。本 章 將 使用系統(tǒng)滴
    發(fā)表于 12-05 14:51 ?9次下載
    SysTick <b class='flag-5'>定時器</b>

    定時器作用實現(xiàn)定時器數(shù)據(jù)結(jié)構(gòu)選取介紹2

    定時器在各種場景都需要用到,比如游戲的Buff實現(xiàn),Redis中的過期任務(wù),Linux中的定時任務(wù)等等。顧名思義,定時器的主要用途是執(zhí)行定時
    的頭像 發(fā)表于 04-21 15:20 ?1383次閱讀
    <b class='flag-5'>定時器</b><b class='flag-5'>作用</b>及<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>定時器</b><b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b><b class='flag-5'>選取</b><b class='flag-5'>介紹</b>2

    什么是軟件定時器?軟件定時器實現(xiàn)原理

    軟件定時器是用程序模擬出來的定時器,可以由一個硬件定時器模擬出成千上萬個軟件定時器,這樣程序在需要使用較多定時器的時候就不會受限于硬件資源的
    的頭像 發(fā)表于 05-23 17:05 ?3346次閱讀

    定時器設(shè)計實現(xiàn)

    返回ITimer類型的共享指針。其中ITimer類中定義了start和stop方法,用于啟動或停止當前定時器。 TimerManager還有一個內(nèi)部類TimerMessageQueue用于實現(xiàn)
    的頭像 發(fā)表于 11-08 16:50 ?875次閱讀

    定時器實現(xiàn)數(shù)據(jù)結(jié)構(gòu)選擇

    在后端的開發(fā)中,定時器有很廣泛的應(yīng)用。 比如: 心跳檢測 倒計時 游戲開發(fā)的技能冷卻 redis的鍵值的有效期等等,都會使用到定時器。 定時器實現(xiàn)
    的頭像 發(fā)表于 11-13 14:22 ?687次閱讀
    <b class='flag-5'>定時器</b>的<b class='flag-5'>實現(xiàn)</b><b class='flag-5'>數(shù)據(jù)結(jié)構(gòu)</b>選擇