日常知識點之遺留問題梳理(定時器/時間輪定時器)

1:簡單基礎

定時器的核心知識點,對我來說就是獲取當前時間和設置回調函數。

簡單練習:

? c語言通過gettimeofday 獲取當前時間并進行處理

? 回調函數的定義(函數參數有必要適當存儲) typedef void(Timerfunc)(void p);

1.1 簡單源碼演示

#include <stdio.h>
#include <sys/time.h>
#include <unistd.h> //sleep
typedef unsigned long int uint64_t;
static uint64_t GetCurrentTime()
{struct timeval tv;gettimeofday(&tv, NULL);return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}void callback(void * p)
{int * i = (int *)p;printf("callback %d\n", *i);
}int main()
{uint64_t mytest = 1;printf("%lu \n", GetCurrentTime());sleep(2);struct timeval tv;gettimeofday(&tv, NULL);printf("second: %ld\n", tv.tv_sec); // 秒printf("millisecond: %ld\n", tv.tv_sec * 1000 + tv.tv_usec / 1000); // 毫秒printf("microsecond: %ld\n", tv.tv_sec * 1000000 + tv.tv_usec); // 徽秒sleep(3); // 讓程序休眠3秒printf("---------------------sleep 3 second-------------------\n");gettimeofday(&tv, NULL);printf("second: %ld\n", tv.tv_sec); // 秒printf("millisecond: %ld\n", tv.tv_sec * 1000 + tv.tv_usec / 1000); // 毫秒printf("microsecond: %ld\n", tv.tv_sec * 1000000 + tv.tv_usec); // 徽秒
//回調函數的簡單定義和使用typedef void(*Timerfunc)(void* p);int func_para = 3;Timerfunc m_func = callback;void * para = (void*)&func_para;(*m_func)(para);return 0;
}

1.2 :運行結果

root@aliy:/home/leetcode# ./get_time1 
1739506868441 
second: 1739506870
millisecond: 1739506870441
microsecond: 1739506870441927
---------------------sleep 3 second-------------------
second: 1739506873
millisecond: 1739506873442
microsecond: 1739506873442060
callback 3

2:借助已有的stl容器實現是最方便的

不知道哪里參考的一個代碼,借助了stl中的一個優先級隊列,就簡單整理一下吧。

回顧好久沒寫的細節:

0:優先級隊列 priority_queue 支持大堆小堆 (自己定義比較函數)

1:鎖和條件變量 條件變量的幾種信號等待方式。

2:chrono下的相關獲取時間的接口需要梳理一下。

3:std::function 和lamba需要回顧練習一下

4:stl的push時可以直接構造結構體對象,task_queue.push(Task{func, exec_time});

5:條件變量中的wait 以及wait_until

? ====》 已經有喚醒 wait用條件等待 防止虛假喚醒

? ====》wait_until 接口可以實現等待到特定時間后進行執行(系統調用內部定時器實現? 會虛假喚醒嗎?)。 和自己代碼實現時間差同功能

在這里插入圖片描述

2.1:練習源碼

都是C++11的東東 需要回顧。

//定時器的簡單實現  借助stl容器,使用線程進行專門的定時器處理。//容器中保存了定時器的超時時間,以及對應的回調函數 
#include <stdio.h>
#include <iostream>
#include <functional>
#include <mutex>
#include <thread>
#include <chrono>
#include <vector>
#include <condition_variable>
#include <atomic>
#include <queue>class Timer{
private:std::thread worker;std::atomic<bool> stop;//鎖和條件變量std::mutex queue_mutex;std::condition_variable condition;struct Task{std::function<void(void)> func;//std::chrono::steady_clock 只能增加的單調時鐘  std::chrono::time_point表示某一刻的對象//這里時間的相關接口需要參考chronostd::chrono::time_point<std::chrono::steady_clock> exec_time;  //為了給wait_until做參數 直接指定bool operator >(const Task & other) const{return exec_time > other.exec_time;}};//優先隊列  用task為元素類型  以std::vector<Task> 進行存儲 按照默認的比較函數進行比較  實現最小堆std::priority_queue<Task, std::vector<Task>, std::greater<Task>> task_queue; //底層是堆的結構private:void run(){while(!stop){//這里進行死循環 或者加鎖條件變量實現隊列中任務的提取if(task_queue.empty()){std::unique_lock<std::mutex> lock(queue_mutex);//防止虛假喚醒condition.wait(lock, [this] {return !this->task_queue.empty() || this->stop;});//這里無法訪問類的成員變量 如何函數內部或者全局變量 即可以// condition.wait(lock, [] {//     return !task_queue.empty() || stop;// });}	if(task_queue.empty() || stop){return;}{// auto now  = std::chrono::steady_clock::now();auto exec_task_time = task_queue.top().exec_time; //目標執行的時間std::unique_lock<std::mutex> lock(queue_mutex);//注意第二個參數 是當前時間加上最大等待時間 if(condition.wait_until(lock, exec_task_time) == std::cv_status::timeout){auto task = task_queue.top();task_queue.pop();lock.unlock();task.func(); //這里沒有定義參數  可以定義參數為自己}}}}public:Timer():stop(false), worker(&Timer::run, this){}~Timer(){ //單例時才把構造函數析構函數設置為私有stop = true;condition.notify_all();worker.join();}static inline time_t get_Clock(){// 獲取當前時間點auto now = std::chrono::steady_clock::now();// 獲取從紀元到現在所經過的持續時間auto duration = now.time_since_epoch();//轉換為毫秒并返回return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();}void schedule(const std::function<void()> &func, int delay_ms){auto exec_time = std::chrono::steady_clock::now() +std::chrono::milliseconds(delay_ms);{std::unique_lock<std::mutex> lock(queue_mutex);task_queue.push(Task{func, exec_time}); //注意這里的細節}condition.notify_all();}
};int main()
{Timer timer;printf("now =   %lu \n", Timer::get_Clock());timer.schedule([](){printf("exec 1000  %lu \n", Timer::get_Clock());}, 1000);timer.schedule([](){printf("exec 3000  %lu \n", Timer::get_Clock());}, 3000);std::this_thread::sleep_for(std::chrono::seconds(5));return 0;
}

2.2 :運行結果

oot@aliy:/home/leetcode# g++ my_timer1.c -o my_timer -std=c++11
root@aliy:/home/leetcode# ./my_timer 
now =   16306525752 
exec 1000  16306526753 
exec 3000  16306528753 

3:總結一些其他遺留

定時器的實現中,往往需要數據結構配合。(時間戳和回調 需要支持排序 需要方便插入)

1:紅黑樹存儲數據結構 比如set map mutilset mutilmap 以及nginx下封裝的紅黑樹。

2:使用最小堆進行存儲。

3:跳表(有序,可以快速插入 參考redis中的跳表源碼)/時間輪定時器。

3.1:時間輪定時器

在這里插入圖片描述

3.2:一個來自別人的demo代碼練習

在這里插入圖片描述

std::vector 模擬數據結構

這樣設計有最大超時時間限制吧,然后輪詢也有精度。

存儲的是節點指針,增加引用計數實現多次執行,類型心跳

//時間輪定時器  采用(數組+鏈表)鏈表結合vector的方式存儲數據結構   采用輪詢的方式處理事件#include <unistd.h>
#include <iostream>
#include <vector>
#include <list>
using namespace std;#include <sys/time.h>//同一個指針對象 多次加入只是引用計數增加  執行次數增加。
class CTimerNode {
public:CTimerNode(int fd) : id(fd), ref(0) {}void Offline() {this->ref = 0;}//通過引用計數的方式  確定是否銷毀該對象  加入時++  消費時--  //可能多次加入定時器 bool TryKill() {if (this->ref == 0) return true;DecrRef();if (this->ref == 0) {cout << id << " is killed down" << endl;return true;}cout << id << " ref is " << ref << endl;return false;}void IncrRef() {this->ref++;}protected:void DecrRef() {this->ref--;}private:int ref;int id;
};const int TW_SIZE = 16;
const int EXPIRE = 10;
const int TW_MASK = TW_SIZE - 1;
static size_t iRealTick = 0;
//鏈表的節點
typedef list<CTimerNode*> TimeList;
typedef TimeList::iterator TimeListIter;
//用vector+list 構造時間輪數據結構 
typedef vector<TimeList> TimeWheel;void AddTimeout(TimeWheel &tw, CTimerNode *p) {if (p) {p->IncrRef();TimeList &le = tw[(iRealTick+EXPIRE) & TW_MASK]; //基于當前的時間  放入對應的list中  le.push_back(p);}
}// 用來表示delay時間后調用 
void AddTimeoutDelay(TimeWheel &tw, CTimerNode *p, size_t delay) {if (p) {p->IncrRef();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];  //每次向前走一個//循環遍歷輪子 消費輪子中的第一個節點對應的list中的所有事件TimeListIter iter = le.begin();for (; iter != le.end();iter++) {CTimerNode *p = *iter;if (p && p->TryKill()) {delete p;}}le.clear();
}static time_t current_time() {time_t t;struct timeval tv;gettimeofday(&tv, NULL);t = (time_t)tv.tv_sec;return t; //這里返回的是秒
}int main ()
{TimeWheel tw(TW_SIZE);CTimerNode *p = new CTimerNode(10001);AddTimeout(tw, p); //加入時間輪定時器中AddTimeoutDelay(tw, p, 5);  //對象已經存在  5s后執行對應的回調time_t start = current_time();for (;;) {time_t now = current_time();//這里以秒為單位   進行依次命中輪詢if (now - start > 0) {for (int i=0; i<now-start; i++)TimerShift(tw);start = now;cout << "check timer shift " << iRealTick <<  endl;}usleep(2500); //2500 微妙  =2.5ms}return 0;
}

3.3 :demo運行

同一個TimerNode節點,只是把指針加入了時間輪中。

每次處理節點時根據引用計數進行判斷了。

root@aliy:/home/leetcode# ./wheel_timer 
check timer shift 1
check timer shift 2
check timer shift 3
check timer shift 4
check timer shift 5
check timer shift 6
check timer shift 7
check timer shift 8
check timer shift 9
check timer shift 10
10001 ref is 1
check timer shift 11
check timer shift 12
check timer shift 13
check timer shift 14
check timer shift 15
10001 is killed down

3.4:更復雜的時間輪

linux內核中使用比較復雜的時間輪來進行定時器的處理

參考時鐘的時針 分針 秒針,多個類似上面的時間輪進行配合,采用不同的精度,配合實現更復雜的功能(只有第一層消費,后面的基層都是按層移動到上一層)。

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895498.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895498.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895498.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python + WhisperX:解鎖語音識別的高效新姿勢

大家好&#xff0c;我是烤鴨&#xff1a; 最近在嘗試做視頻的質量分析&#xff0c;打算利用asr針對聲音判斷是否有人聲&#xff0c;以及識別出來的文本進行進一步操作。asr看了幾個開源的&#xff0c;最終選擇了openai的whisper&#xff0c;后來發現性能不行&#xff0c;又換了…

$ npx electron-forge import 一直報權限問題 resource busy or locked,

jackLAPTOP-7DHDAAL0 MINGW64 /e/project/celetron-project/my-electron-app (master) $ npx electron-forge import > Checking your system > Checking git exists > Checking node version > Checking packageManager version √ Found node22.14.0 √ Found gi…

mapbox 從入門到精通 - 目錄

&#x1f468;??? 主頁&#xff1a; gis分享者 &#x1f468;??? 感謝各位大佬 點贊&#x1f44d; 收藏? 留言&#x1f4dd; 加關注?! &#x1f468;??? 收錄于專欄&#xff1a;mapbox 從入門到精通 文章目錄 一、&#x1f340;總目錄1.1 ?? mapbox基礎1.2 ??…

Kotlin 2.1.0 入門教程(十五)繼承、重寫、派生類初始化順序

繼承 所有類都有一個共同的超類 Any&#xff0c;對于沒有聲明超類型的類來說&#xff0c;Any 是其默認的超類&#xff1a; // 隱式繼承自 Any。 class ExampleAny 有三個方法&#xff1a;equals()、hashCode() 和 toString()。因此&#xff0c;所有類都定義了這些方法。 默認…

sqlilabs--小實驗

一、先盲注判斷 ?id1 and sleep(2)-- 如果發現頁面存在注點&#xff0c;使用時間盲注腳本進行注入 import requestsdef inject_database(url):name for i in range(1, 20): # 假設數據庫名稱長度不超過20low 48 # 0high 122 # zmiddle (low high) // 2while low &l…

【數字】異步FIFO面試的幾個小問題與跨時鐘域時序約束

入門數字設計的時候&#xff0c;跨時鐘域的數據處理是繞不開的課題&#xff0c;特別是多比特數據跨時鐘域時&#xff0c;都會采用異步FIFO的方法。 異步FIFO中涉及較多的考點這里記錄幾個以供大家參考。 1. 異步FIFO的空滿判斷分別在哪個域&#xff1f; 根據異步FIFO的結構&…

淺談Java Spring Boot 框架分析和理解

Spring Boot是一個簡化Spring開發的框架&#xff0c;它遵循“約定優于配置”的原則&#xff0c;通過內嵌的Tomcat、Jetty或Undertow等容器&#xff0c;使得開發者能夠快速構建獨立運行的、生產級別的基于Spring框架的應用程序。Spring Boot包含了大量的自動配置功能&#xff0c…

算法06-回溯算法

一、回溯算法詳解 回溯算法是一種通過逐步構建解決方案來解決問題的算法。它通常用于解決組合問題、排列問題、子集問題等。回溯算法的核心思想是“試錯”&#xff0c;即在每一步嘗試所有可能的選項&#xff0c;如果發現當前選擇無法達到目標&#xff0c;就回退到上一步&#…

RabbitMQ學習—day2—安裝

目錄 普通Linux安裝 安裝RabbitMQ 1、下載 2、安裝 3. Web管理界面及授權操作 Docker 安裝 強力推薦學docker&#xff0c;使用docker安裝 普通Linux安裝 安裝RabbitMQ 1、下載 官網下載地址&#xff1a;https://www.rabbitmq.com/download.html(opens new window) 這…

降本增效 - VGF 構建輕量高性能日志管理平臺

VFG 技術架構 Filebeat 接收Syslog &#xff0c;并進行日志分段&#xff0c;VictoriaLogs 持久化存儲日志 &#xff0c;Grafana 可視化、數據查詢、告警、數據導出。 為什么要用VictoriaLogs &#xff1f; 與Elasticsearch /Grafana Loki相比幾十倍的CPU/內存/存儲資源占用的…

初識camel智能體(一)

同目錄下配置環境變量.env&#xff0c;內容如下&#xff0c; apikey從魔搭社區獲取 QWEN_API_KEY4ff3ac8f-aebc******** 先上干貨代碼&#xff0c;主代碼如下&#xff1a; from colorama import Forefrom camel.societies import RolePlaying from camel.utils import prin…

介紹 Liquibase、Flyway、Talend 和 Apache NiFi:選擇適合的工具

在現代軟件開發中&#xff0c;尤其是在數據庫管理和數據集成方面&#xff0c;選擇合適的工具至關重要。本文將介紹四個流行的工具&#xff1a;Liquibase、Flyway、Talend 和 Apache NiFi&#xff0c;分析它們的應用、依賴以及如何選擇適合的工具。 1. Liquibase 簡介&#xff…

Docker使用指南與Dockerfile文件詳解:從入門到實戰

Docker使用指南與Dockerfile文件詳解:從入門到實戰 文章目錄 **Docker使用指南與Dockerfile文件詳解:從入門到實戰****引言****第一部分:Docker 核心概念速覽****1. Docker 基礎架構****2. Docker 核心命令****第二部分:Dockerfile 文件深度解析****1. Dockerfile 是什么?…

Qt工作總結03 <qSort按某一屬性進行排序>

1. 代碼樣例 QList<QGraphicsTextItem *> Lst;qSort(Lst.begin(),Lst.end(),[](const QGraphicsTextItem *itemA,const QGraphicsTextItem *itemB) {return itemA->toPlainText().toDouble() < itemB->toPlainText().toDouble(); }); 2. 參考 QList 按結構體…

深度學習|表示學習|Instance Normalization 全面總結|26

如是我聞&#xff1a; 1. Instance Normalization&#xff08;IN&#xff09; Instance Normalization&#xff08;IN&#xff09;最早由 Ulyanov et al.&#xff08;2017&#xff09; 提出&#xff0c;主要用于 風格遷移&#xff08;Style Transfer&#xff09; 任務。它的核…

如何保持 mysql 和 redis 中數據的一致性?PegaDB 給出答案

MySQL 與 Redis 數據保持一致性是一個常見且復雜的問題&#xff0c;一般來說需要結合多種策略來平衡性能與一致性。 傳統的解決策略是先讀緩存&#xff0c;未命中則讀數據庫并回填緩存&#xff0c;但方式這種維護成本較高。 隨著云數據庫技術的發展&#xff0c;目前國內云廠商…

探索ELK 的魅力

在大數據時代&#xff0c;海量日志和數據的收集、存儲、處理與可視化分析變得越來越重要。而 ELK 堆棧&#xff0c;由 Elasticsearch、Logstash、Beats 和 Kibana 組成&#xff0c;正是一個強大的開源解決方案&#xff0c;幫助開發者和運維人員高效管理和分析日志數據。本文將詳…

用vue3寫一個好看的wiki前端頁面

以下是一個使用 Vue 3 Element Plus 實現的 Wiki 風格前端頁面示例&#xff0c;包含現代設計、響應式布局和常用功能&#xff1a; <template><div class"wiki-container"><!-- 頭部導航 --><el-header class"wiki-header"><d…

深度學習實戰基礎案例——卷積神經網絡(CNN)基于DenseNet的眼疾檢測|第4例

文章目錄 前言一、數據準備二、項目實戰2.1 設置GPU2.2 數據加載2.3 數據預處理2.4 數據劃分2.5 搭建網絡模型2.6 構建densenet1212.7 訓練模型2.8 結果可視化 三、UI設計四、結果展示總結 前言 在當今社會&#xff0c;眼科疾病尤其是白內障對人們的視力健康構成了嚴重威脅。白…

DeepSeek的開源核爆:當技術民主化重構AI權力版圖

2025年2月&#xff0c;全球AI產業正經歷著由DeepSeek掀起的鏈式反應——這個首個開源千億參數多模態模型的企業&#xff0c;用開放戰略在技術壁壘森嚴的AI戰場投下"制度性核彈"。其貢獻不在于單純的技術突破&#xff0c;而在于通過開源協議實現了三重維度的大爆炸&am…