C++定時(shí)器實(shí)現(xiàn)和時(shí)間輪介紹
定時(shí)器
有些時(shí)候我們需要延遲執(zhí)行一些功能,比如每10s進(jìn)行一次數(shù)據(jù)采集。或者告知用戶技能冷卻有多少時(shí)間,如果我們將執(zhí)行這些功能的任務(wù)交給主線程,就會(huì)造成主線程的阻塞。因此我們可以選擇一個(gè)創(chuàng)建一個(gè)子線程,讓其檢測(cè)定時(shí)器中的任務(wù),當(dāng)有任務(wù)的時(shí)間到了的時(shí)候,就去執(zhí)行這個(gè)任務(wù)。
最小堆實(shí)現(xiàn)定時(shí)器
定時(shí)器可以由很多種數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn),比如最小堆、紅黑樹、跳表、甚至數(shù)組都可以,其本質(zhì)都是拿到最小時(shí)間的任務(wù),然后取出該任務(wù)并執(zhí)行。
綜合實(shí)現(xiàn)難度和效率來(lái)看,最小堆是最容易實(shí)現(xiàn)定時(shí)器的數(shù)據(jù)結(jié)構(gòu)。
最小堆主要有以下接口:
#pragma once #include <vector> #include <map> using namespace std; typedef void (*TimerHandler)(struct TimerNode *node); //獲取系統(tǒng)時(shí)間,單位是毫秒 static uint32_t current_time() { uint32_t t; struct timespec ti; clock_gettime(CLOCK_MONOTONIC, &ti); t = (uint32_t)ti.tv_sec * 1000; t += ti.tv_nsec / 1000000; return t; } struct TimerNode { //該任務(wù)在最小堆中的下標(biāo)位置 int idx = 0; //該任務(wù)是第幾號(hào)任務(wù) int id = 0; //幾毫秒后執(zhí)行該任務(wù) unsigned int expire = 0; //回調(diào)函數(shù) TimerHandler cb = NULL; }; class MinHeapTimer { public: MinHeapTimer() { _heap.clear(); _map.clear(); } int Count(); //加入任務(wù),expire為該任務(wù)的失效時(shí)間,expire過后就要執(zhí)行回調(diào)函數(shù)cb int AddTimer(uint32_t expire, TimerHandler cb); //刪除一個(gè)任務(wù) bool DelTimer(int id); //獲取一個(gè)任務(wù) void ExpireTimer(); private: //用于比較兩個(gè)任務(wù)的過期時(shí)間 bool _compare(int lhs, int rhs); //向下調(diào)整算法,每次刪除一個(gè)節(jié)點(diǎn)就要向下調(diào)整 void _shiftDown(int parent); //向上調(diào)整算法,每添加一個(gè)數(shù)都要調(diào)用向上調(diào)整算法,保證根節(jié)點(diǎn)為最小節(jié)點(diǎn) void _shiftUp(int child); //刪除的子函數(shù) void _delNode(TimerNode *node); void resign(int pos); private: //數(shù)組中存儲(chǔ)任務(wù)節(jié)點(diǎn) vector<TimerNode *> _heap; //存儲(chǔ)值和響應(yīng)節(jié)點(diǎn)的映射關(guān)系 map<int, TimerNode *> _map; //任務(wù)的個(gè)數(shù),注意不是_heap的size int _count = 0; };
具體的實(shí)現(xiàn)以及測(cè)試為:
#pragma once #include <vector> #include <map> using namespace std; typedef void (*TimerHandler)(struct TimerNode *node); //獲取系統(tǒng)時(shí)間,單位是毫秒 static uint32_t current_time() { uint32_t t; struct timespec ti; clock_gettime(CLOCK_MONOTONIC, &ti); t = (uint32_t)ti.tv_sec * 1000; t += ti.tv_nsec / 1000000; return t; } struct TimerNode { //該任務(wù)在最小堆中的下標(biāo)位置 int idx = 0; //該任務(wù)是第幾號(hào)任務(wù) int id = 0; unsigned int expire = 0; //回調(diào)函數(shù) TimerHandler cb = NULL; }; class MinHeapTimer { public: MinHeapTimer() { _heap.clear(); _map.clear(); } int Count() { return ++_count; } //加入任務(wù),expire為該任務(wù)的失效時(shí)間,expire過后就要執(zhí)行回調(diào)函數(shù)cb int AddTimer(uint32_t expire, TimerHandler cb) { int64_t timeout = current_time() + expire; TimerNode *node = new TimerNode; int id = Count(); node->id = id; node->expire = timeout; node->cb = cb; node->idx = (int)_heap.size(); _heap.push_back(node); _shiftUp((int)_heap.size() - 1); _map.insert(make_pair(id, node)); return id; } //刪除一個(gè)任務(wù) bool DelTimer(int id) { auto iter = _map.find(id); if (iter == _map.end()) return false; _delNode(iter->second); return true; } //獲取一個(gè)任務(wù) void ExpireTimer() { if (_heap.empty()) { return; } //獲取當(dāng)前時(shí)間 uint32_t now = current_time(); while (!_heap.empty()) { //獲取最近的一個(gè)任務(wù) TimerNode *node = _heap.front(); //當(dāng)最近一個(gè)任務(wù)的時(shí)間大于當(dāng)前時(shí)間,說明沒有任務(wù)要執(zhí)行 if (now < node->expire) { break; } //遍歷一下堆,這一步可以不加 for (int i = 0; i < _heap.size(); i++) { std::cout << "touch idx: " << _heap[i]->idx << " id: " << _heap[i]->id << " expire: " << _heap[i]->expire << std::endl; } //執(zhí)行最近任務(wù)的回調(diào)函數(shù) if (node->cb) { node->cb(node); } //執(zhí)行完就刪掉這個(gè)任務(wù) _delNode(node); } } private: //用于比較兩個(gè)任務(wù)的過期時(shí)間 bool _compare(int lhs, int rhs) { return _heap[lhs]->expire < _heap[rhs]->expire; } //向下調(diào)整算法,每次刪除一個(gè)節(jié)點(diǎn)就要向下調(diào)整 void _shiftDown(int parent) { int child = parent * 2 + 1; while (child < _heap.size() - 1) { if (child + 1 < _heap.size() - 1 && !_compare(child, child + 1)) { child++; } if (!_compare(parent, child)) { std::swap(_heap[parent], _heap[child]); _heap[parent]->idx = parent; _heap[child]->idx = child; parent = child; child = parent * 2 + 1; } else { break; } } } //向上調(diào)整算法,每添加一個(gè)數(shù)都要調(diào)用向上調(diào)整算法,保證根節(jié)點(diǎn)為最小節(jié)點(diǎn) void _shiftUp(int child) { int parent = (child - 1) / 2; while (child > 0) { if (!_compare(parent, child)) { std::swap(_heap[parent], _heap[child]); _heap[parent]->idx = parent; _heap[child]->idx = child; child = parent; parent = (child - 1) / 2; } else { break; } } } //刪除的子函數(shù) void _delNode(TimerNode *node) { int last = (int)_heap.size() - 1; int idx = node->idx; if (idx != last) { std::swap(_heap[idx], _heap[last]); _heap[idx]->idx = idx; resign(idx); } _heap.pop_back(); _map.erase(node->id); delete node; } void resign(int pos) { //向上調(diào)整和向下調(diào)整只會(huì)發(fā)生一個(gè) _shiftDown(pos); _shiftUp(pos); } private: //數(shù)組中存儲(chǔ)任務(wù)節(jié)點(diǎn) vector<TimerNode *> _heap; //存儲(chǔ)值和響應(yīng)節(jié)點(diǎn)的映射關(guān)系 map<int, TimerNode *> _map; //任務(wù)的個(gè)數(shù),注意不是_heap的size int _count = 0; };
#include <time.h> #include <unistd.h> #include <iostream> #include "minheap.h" void print_hello(TimerNode *te) { std::cout << "hello world time = " << te->idx << "\t" << te->id << "\t" << current_time() << std::endl; } int main() { MinHeapTimer mht; //一號(hào)任務(wù),立刻執(zhí)行 mht.AddTimer(0, print_hello); //二號(hào)任務(wù),一秒后執(zhí)行 mht.AddTimer(1000, print_hello); mht.AddTimer(7000, print_hello); mht.AddTimer(2000, print_hello); mht.AddTimer(9000, print_hello); mht.AddTimer(10000, print_hello); mht.AddTimer(6000, print_hello); mht.AddTimer(3000, print_hello); while (1) { mht.ExpireTimer(); // usleep(10000); sleep(1); } return 0; }
時(shí)間輪
上面的定時(shí)器在任務(wù)數(shù)量很多時(shí),效率會(huì)很低,因?yàn)槲覀冃枰米钚《褋?lái)維護(hù)這些任務(wù),并且每刪除一個(gè)任務(wù),都要進(jìn)行調(diào)整。其主要原因是我們不知道其他任務(wù)什么時(shí)候執(zhí)行,所以我們只能進(jìn)行調(diào)整,將最近的任務(wù)放到堆頂。
如果我們能夠向哈希桶那樣,將要執(zhí)行的任務(wù)形成鏈表,掛到要執(zhí)行的位置,當(dāng)時(shí)間走到那個(gè)位置的時(shí)候,就執(zhí)行這些任務(wù),效率豈不是更高?
單層級(jí)時(shí)間輪
客戶端每 5 秒鐘發(fā)送?跳包;服務(wù)端若 10 秒內(nèi)沒收到?跳數(shù)據(jù),則清除連接。
考慮到正常情況下 5 秒鐘發(fā)送?次?跳包,10 秒才檢測(cè)?次,如下圖到索引為 10 的時(shí)候并不能踢掉連接;所以需要每收到?次?跳包則 used++ ,每檢測(cè)?次 used-- ;當(dāng)檢測(cè)到used == 0 則踢掉連接;
#include <unistd.h> #include <stdio.h> #include <string.h> #include <stdint.h> #include <sys/time.h> #include <time.h> #define MAX_CONN ((1 << 16) - 1) //連接的節(jié)點(diǎn),用來(lái)記錄心跳包發(fā)送的次數(shù) typedef struct conn_node { struct conn_node *next; //引用計(jì)數(shù)的次數(shù),當(dāng)used==0,相當(dāng)于自動(dòng)銷毀 uint8_t used; int id; } conn_node_t; //用數(shù)組記錄所有的連接 static conn_node_t nodes[MAX_CONN] = {0}; static uint32_t iter = 0; //獲取一個(gè)空的連接節(jié)點(diǎn) conn_node_t *get_node() { iter++; while (nodes[iter & MAX_CONN].used > 0) { iter++; } return &nodes[iter]; } //哈希桶的個(gè)數(shù) #define TW_SIZE 16 //檢測(cè)心跳包的延遲時(shí)間,由于哈希桶的個(gè)數(shù)有限,所以心跳包發(fā)送時(shí)間不能夠超過6 #define EXPIRE 10 //取余操作 #define TW_MASK (TW_SIZE - 1) static uint32_t tick = 0; //哈希桶 typedef struct link_list { conn_node_t head; //一個(gè)tail,能夠進(jìn)行快速插入 conn_node_t *tail; } link_list_t; //添加連接 void add_conn(link_list_t *tw, conn_node_t *node, int delay) { //獲取對(duì)應(yīng)的哈希桶 link_list_t *list = &tw[(tick + EXPIRE + delay) & TW_MASK]; list->tail->next = node; list->tail = node; node->next = NULL; node->used++; } //清楚這個(gè)哈希桶 void link_clear(link_list_t *list) { list->head.next = NULL; list->tail = &(list->head); } //檢測(cè)哈希桶 void check_conn(link_list_t *tw) { int32_t itick = tick; tick++; //取到對(duì)應(yīng)哈希桶的鏈表 link_list_t *list = &tw[itick & TW_MASK]; //檢測(cè)哈希桶對(duì)應(yīng)的鏈表 conn_node_t *current = list->head.next; while (current) { conn_node_t *temp = current; current = current->next; temp->used--; if (temp->used == 0) { printf("連接:%d 斷開\n", temp->id); temp->next = NULL; continue; } printf("這個(gè)鏈接:%d 心跳包還剩:%d個(gè)需要檢測(cè)\n", temp->id, temp->used); } link_clear(list); } //獲取時(shí)間,單位是s static time_t current_time() { time_t t; struct timespec ti; clock_gettime(CLOCK_MONOTONIC, &ti); t = (time_t)ti.tv_sec; return t; } int main() { memset(nodes, 0, MAX_CONN * sizeof(conn_node_t)); // init link list link_list_t tw[TW_SIZE]; memset(tw, 0, TW_SIZE * sizeof(link_list_t)); for (int i = 0; i < TW_SIZE; i++) { link_clear(&tw[i]); } // 第一個(gè)連接對(duì)應(yīng)的心跳包,在0和5時(shí)進(jìn)行發(fā)送 //所以會(huì)在10s和15s時(shí)進(jìn)行檢測(cè),15s時(shí)把該連接斷開 { conn_node_t *node = get_node(); node->id = 10001; add_conn(tw, node, 0); add_conn(tw, node, 5); } //第二個(gè)連接發(fā)送的心跳包,在第10s時(shí)進(jìn)行檢測(cè) { conn_node_t *node = get_node(); node->id = 10002; add_conn(tw, node, 0); } //第二個(gè)連接發(fā)送的心跳包,在第13s時(shí)檢測(cè) { conn_node_t *node = get_node(); node->id = 10003; add_conn(tw, node, 3); } time_t start = current_time(); while (1) { time_t now = current_time(); if (now - start > 0) { for (int i = 0; i < now - start; i++) check_conn(tw); start = now; printf("在第%d秒時(shí)檢測(cè),此時(shí)機(jī)器時(shí)間:%d\n", tick, now); } } return 0; }
上面的時(shí)間輪受哈希桶大小和延遲10s收到心跳包的影響,只能在[0,6]秒內(nèi)發(fā)送數(shù)據(jù)心跳包,如果想要延遲長(zhǎng)時(shí)間,則需要擴(kuò)大哈希桶的大小。
如果我們不發(fā)送心跳包,而是改成在若干秒后執(zhí)行一個(gè)任務(wù),比如50s后執(zhí)行任務(wù),但哈希桶大小只有16,我們可以在任務(wù)節(jié)點(diǎn)中增加一個(gè)參數(shù)round,用來(lái)記錄需要走多少遍哈希桶,比如50s,對(duì)應(yīng)到大小為16的哈希桶則round=3,idx=2。
不過這樣做,在check_conn取任務(wù)時(shí)就不能夠把整個(gè)鏈表都取出來(lái),而是需要取出round==0的任務(wù)。
typedef struct node { struct conn_node *next; //需要走多少輪哈希桶,當(dāng)round==0時(shí),則說明需要執(zhí)行這個(gè)任務(wù) int round; int id; } node_t;
這樣做能解決時(shí)間輪刻度范圍過大造成的空間浪費(fèi),但是卻帶來(lái)了另一個(gè)問題:時(shí)間輪每次都需要遍歷任務(wù)列表,耗時(shí)增加,當(dāng)時(shí)間輪刻度粒度很小(秒級(jí)甚至毫秒級(jí)),任務(wù)列表又特別長(zhǎng)時(shí),這種遍歷的辦法是不可接受的。
多層級(jí)時(shí)間輪
參照時(shí)鐘表盤的運(yùn)轉(zhuǎn)規(guī)律,可以將定時(shí)任務(wù)根據(jù)觸發(fā)的緊急程度,分布到不同層級(jí)的時(shí)間輪中;
假設(shè)時(shí)間精度為 10ms ;在第 1 層級(jí)每 10ms 移動(dòng)?格;每移動(dòng)?格執(zhí)?該格?當(dāng)中所有的定時(shí)任務(wù);
當(dāng)?shù)?1 層指針從 255 格開始移動(dòng),此時(shí)層級(jí) 2 移動(dòng)?格;層級(jí) 2 移動(dòng)?格的?為定義為,將該格當(dāng)中的定時(shí)任務(wù)重新映射到層級(jí) 1 當(dāng)中;同理,層級(jí) 2 當(dāng)中從 63 格開始移動(dòng),層級(jí) 3 格?中的定時(shí)任務(wù)重新映射到層級(jí) 2 ; 以此類推層級(jí) 4 往層級(jí) 3 映射,層級(jí) 5 往層級(jí) 4 映射。
只有任務(wù)在第一層時(shí)才會(huì)被執(zhí)行,其他層都是將任務(wù)重新映射到上一層。
timewheel.h:
#pragma once #include <stdint.h> #define TIME_NEAR_SHIFT 8 #define TIME_NEAR (1 << TIME_NEAR_SHIFT) #define TIME_LEVEL_SHIFT 6 #define TIME_LEVEL (1 << TIME_LEVEL_SHIFT) #define TIME_NEAR_MASK (TIME_NEAR - 1) #define TIME_LEVEL_MASK (TIME_LEVEL - 1) typedef struct timer_node timer_node_t; typedef void (*handler_pt)(struct timer_node *node); struct timer_node { struct timer_node *next; uint32_t expire; //時(shí)間 handler_pt callback; //回調(diào)函數(shù) uint8_t cancel; //是否刪除 int id; // 此時(shí)攜帶參數(shù) }; timer_node_t *add_timer(int time, handler_pt func, int threadid); void expire_timer(void); void del_timer(timer_node_t *node); void init_timer(void); void clear_timer();
spinlock.h:
#pragma once struct spinlock { int lock; }; void spinlock_init(struct spinlock *lock) { lock->lock = 0; } void spinlock_lock(struct spinlock *lock) { while (__sync_lock_test_and_set(&lock->lock, 1)) { } } int spinlock_trylock(struct spinlock *lock) { return __sync_lock_test_and_set(&lock->lock, 1) == 0; } void spinlock_unlock(struct spinlock *lock) { __sync_lock_release(&lock->lock); } void spinlock_destroy(struct spinlock *lock) { (void)lock; }
timewheel.cpp:
#include "spinlock.h" #include "timewheel.h" #include <string.h> #include <stddef.h> #include <stdlib.h> #include <time.h> typedef struct link_list { timer_node_t head; timer_node_t *tail; } link_list_t; //多級(jí)時(shí)間輪 typedef struct timer { //第一級(jí) link_list_t near[TIME_NEAR]; // 2-4級(jí) link_list_t t[4][TIME_LEVEL]; struct spinlock lock; uint32_t time; uint64_t current; uint64_t current_point; } s_timer_t; static s_timer_t *TI = NULL; timer_node_t *link_clear(link_list_t *list) { timer_node_t *ret = list->head.next; list->head.next = 0; list->tail = &(list->head); return ret; } //鏈接一個(gè)節(jié)點(diǎn) void link(link_list_t *list, timer_node_t *node) { list->tail->next = node; list->tail = node; node->next = 0; } void add_node(s_timer_t *T, timer_node_t *node) { uint32_t time = node->expire; uint32_t current_time = T->time; uint32_t msec = time - current_time; //根據(jù)時(shí)間 if (msec < TIME_NEAR) { //[0, 0x100) link(&T->near[time & TIME_NEAR_MASK], node); } else if (msec < (1 << (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT))) { //[0x100, 0x4000) link(&T->t[0][((time >> TIME_NEAR_SHIFT) & TIME_LEVEL_MASK)], node); } else if (msec < (1 << (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT))) { //[0x4000, 0x100000) link(&T->t[1][((time >> (TIME_NEAR_SHIFT + TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node); } else if (msec < (1 << (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT))) { //[0x100000, 0x4000000) link(&T->t[2][((time >> (TIME_NEAR_SHIFT + 2 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node); } else { //[0x4000000, 0xffffffff] link(&T->t[3][((time >> (TIME_NEAR_SHIFT + 3 * TIME_LEVEL_SHIFT)) & TIME_LEVEL_MASK)], node); } } //增加事件 timer_node_t *add_timer(int time, handler_pt func, int threadid) { timer_node_t *node = (timer_node_t *)malloc(sizeof(*node)); spinlock_lock(&TI->lock); node->expire = time + TI->time; // 每10ms加1 0 node->callback = func; node->id = threadid; if (time <= 0) { node->callback(node); free(node); spinlock_unlock(&TI->lock); return NULL; } add_node(TI, node); spinlock_unlock(&TI->lock); return node; } void move_list(s_timer_t *T, int level, int idx) { timer_node_t *current = link_clear(&T->t[level][idx]); while (current) { timer_node_t *temp = current->next; add_node(T, current); current = temp; } } void timer_shift(s_timer_t *T) { int mask = TIME_NEAR; uint32_t ct = ++T->time; if (ct == 0) { move_list(T, 3, 0); } else { // ct / 256 uint32_t time = ct >> TIME_NEAR_SHIFT; int i = 0; // ct % 256 == 0 while ((ct & (mask - 1)) == 0) { int idx = time & TIME_LEVEL_MASK; if (idx != 0) { move_list(T, i, idx); break; } mask <<= TIME_LEVEL_SHIFT; time >>= TIME_LEVEL_SHIFT; ++i; } } } void dispatch_list(timer_node_t *current) { do { timer_node_t *temp = current; current = current->next; if (temp->cancel == 0) temp->callback(temp); free(temp); } while (current); } void timer_execute(s_timer_t *T) { int idx = T->time & TIME_NEAR_MASK; while (T->near[idx].head.next) { timer_node_t *current = link_clear(&T->near[idx]); spinlock_unlock(&T->lock); dispatch_list(current); spinlock_lock(&T->lock); } } void timer_update(s_timer_t *T) { spinlock_lock(&T->lock); timer_execute(T); timer_shift(T); timer_execute(T); spinlock_unlock(&T->lock); } void del_timer(timer_node_t *node) { node->cancel = 1; } s_timer_t *timer_create_timer() { s_timer_t *r = (s_timer_t *)malloc(sizeof(s_timer_t)); memset(r, 0, sizeof(*r)); int i, j; for (i = 0; i < TIME_NEAR; i++) { link_clear(&r->near[i]); } for (i = 0; i < 4; i++) { for (j = 0; j < TIME_LEVEL; j++) { link_clear(&r->t[i][j]); } } spinlock_init(&r->lock); r->current = 0; return r; } uint64_t gettime() { uint64_t t; struct timespec ti; clock_gettime(CLOCK_MONOTONIC, &ti); t = (uint64_t)ti.tv_sec * 100; t += ti.tv_nsec / 10000000; return t; } void expire_timer(void) { uint64_t cp = gettime(); if (cp != TI->current_point) { uint32_t diff = (uint32_t)(cp - TI->current_point); TI->current_point = cp; int i; for (i = 0; i < diff; i++) { timer_update(TI); } } } void init_timer(void) { TI = timer_create_timer(); TI->current_point = gettime(); } void clear_timer() { int i, j; for (i = 0; i < TIME_NEAR; i++) { link_list_t *list = &TI->near[i]; timer_node_t *current = list->head.next; while (current) { timer_node_t *temp = current; current = current->next; free(temp); } link_clear(&TI->near[i]); } for (i = 0; i < 4; i++) { for (j = 0; j < TIME_LEVEL; j++) { link_list_t *list = &TI->t[i][j]; timer_node_t *current = list->head.next; while (current) { timer_node_t *temp = current; current = current->next; free(temp); } link_clear(&TI->t[i][j]); } } }
tw-timer.cpp:
#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <time.h> #include <stdlib.h> #include "timewheel.h" struct context { int quit; int thread; }; struct thread_param { struct context *ctx; int id; }; static struct context ctx = {0}; void do_timer(timer_node_t *node) { printf("timer expired:%d - thread-id:%d\n", node->expire, node->id); } void *thread_worker(void *p) { struct thread_param *tp = (struct thread_param *)p; int id = tp->id; struct context *ctx = tp->ctx; while (!ctx->quit) { int expire = rand() % 200; add_timer(expire, do_timer, id); usleep(expire * (10 - 1) * 1000); } printf("thread_worker:%d exit!\n", id); return NULL; } void do_quit(timer_node_t *node) { ctx.quit = 1; } int main() { srand(time(NULL)); ctx.thread = 8; pthread_t pid[ctx.thread]; init_timer(); add_timer(6000, do_quit, 100); struct thread_param task_thread_p[ctx.thread]; int i; for (i = 0; i < ctx.thread; i++) { task_thread_p[i].id = i; task_thread_p[i].ctx = &ctx; if (pthread_create(&pid[i], NULL, thread_worker, &task_thread_p[i])) { fprintf(stderr, "create thread failed\n"); exit(1); } } while (!ctx.quit) { expire_timer(); usleep(2500); } clear_timer(); for (i = 0; i < ctx.thread; i++) { pthread_join(pid[i], NULL); } printf("all thread is closed\n"); return 0; }
到此這篇關(guān)于C++定時(shí)器實(shí)現(xiàn)和時(shí)間輪介紹的文章就介紹,到這了,更多相關(guān)C++定時(shí)器 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++11實(shí)現(xiàn)簡(jiǎn)易定時(shí)器的示例代碼
- C++定時(shí)器Timer在項(xiàng)目中的使用方法
- C++控制臺(tái)用定時(shí)器實(shí)例代碼
- C++?超詳細(xì)分析數(shù)據(jù)結(jié)構(gòu)中的時(shí)間復(fù)雜度
- C++11時(shí)間日期庫(kù)chrono的使用
- c++11多種格式時(shí)間轉(zhuǎn)化為字符串的方法實(shí)現(xiàn)
- C++使用chrono庫(kù)處理日期和時(shí)間的實(shí)現(xiàn)方法
- 最短時(shí)間學(xué)會(huì)基于C++實(shí)現(xiàn)DFS深度優(yōu)先搜索
相關(guān)文章
C語(yǔ)言判斷一個(gè)數(shù)是否為素?cái)?shù)方法解析
這篇文章主要介紹了C語(yǔ)言判斷一個(gè)數(shù)是否為素?cái)?shù)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07C語(yǔ)言修煉之路靈根孕育源流出?初識(shí)C言大道生上篇
C語(yǔ)言是一門面向過程、抽象化的通用程序設(shè)計(jì)語(yǔ)言,廣泛應(yīng)用于底層開發(fā)。C語(yǔ)言能以簡(jiǎn)易的方式編譯、處理低級(jí)存儲(chǔ)器。C語(yǔ)言是僅產(chǎn)生少量的機(jī)器語(yǔ)言以及不需要任何運(yùn)行環(huán)境支持便能運(yùn)行的高效率程序設(shè)計(jì)語(yǔ)言2022-03-03C語(yǔ)言中實(shí)現(xiàn)“17進(jìn)制”轉(zhuǎn)“10進(jìn)制”實(shí)例代碼
這篇文章主要介紹了C語(yǔ)言中實(shí)現(xiàn)“17進(jìn)制”轉(zhuǎn)“10進(jìn)制”實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-05-05C語(yǔ)言Iniparser庫(kù)實(shí)現(xiàn)ini文件讀寫
iniparser是針對(duì)INI文件的解析器。ini文件則是一些系統(tǒng)或者軟件的配置文件。本文就來(lái)介紹一下如何利用Iniparser庫(kù)實(shí)現(xiàn)ini文件讀寫吧2023-03-03C語(yǔ)言的常量,字符串,轉(zhuǎn)義字符,注釋你都了解嗎
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言的常量,字符串,轉(zhuǎn)義字符,注釋,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-02-02