服務端定時器的學習(一)

一、定時器

1、定時器是什么?

定時器不僅存在于硬件領域,在軟件層面(客戶端、網頁和服務端)也普遍應用,核心功能都是高效管理大量延時任務。不同應用場景下,其實現方式和使用方法有所差異。

2、定時器解決了什么問題?

可以定期清理緩存,定時備份數據,在服務器負載較大時,自動執行一些重要的功能,提高服務器的效率和穩定性。

3、是怎么解決的?

  • 組織大量延時任務的數據結構(容器)
  • 觸發最近將超時的任務的機制

4、實現方式:

  • 對任務按觸發時間進行排序:紅黑樹(map,set,multimap,multiset)–nginx,最小堆–libevent,libev,go,應用在單線程場景下
    • 1、觸發時刻作為key,任務作為val
    • 2、快速找到最近要超時的任務
    • 3、觸發后要刪除該任務且支持隨時刪除任務
    • 4、允許相同時刻觸發任務
  • 對執行順序進行組織:時間輪,針對當前時間指針做偏移。–netty,skynet,kafka,應用在多線程場景下

5、有哪些常用觸發機制?

  • I/O多路復用的最后一個超時參數
  • 將定時器轉化為io處理,timerfd

6、使用場景

  • 與網絡模塊協同處理
  • 基于事件驅動業務開展
  • 除了協同網絡處理,復用系統調用

二、具體實現

1、采用紅黑樹,對任務按觸發時間進行排序

  • map<key, value>: 以key存觸發時間,value存任務,那么可能存在多個同一時刻的任務,不選
  • multimap<key, value>:可能存儲重復的值,然后操作起來比較麻煩,不選
  • set: 不可能出現重復的,可以
    那么以一個自定義結構作為key值進行存儲,并且按觸發時間進行排序
typedef struct TimerNode_S{time_t expire;//過期時間uint64_t id;    //由于可能存在多個定時器在同一時間過期,所以需要一個唯一標識}TimerNode_S;bool operator < (const TimerNode_S& lhd, const TimerNode_S& rhd)
{if(lhd.expire < rhd.expire){return true;}else if(lhd.expire > rhd.expire){return false;}else{      //如果相等,誰先插入,誰就先執行return lhd.id < rhd.id;}
}set<TimerNode, less<>> timeouts;

2、計算最近觸發的定時任務離當前還有多久?

time_t TimeOut() 
{auto iter = timeouts.begin();if (iter == timeouts.end()) {return -1;}time_t t = iter->expire - GetTick();return t > 0 ? t : 0;
}

3、獲取當前時間

/**
* @brief 獲取當前時間的時間戳(以毫秒為單位)
*
* 使用 std::chrono::steady_clock 獲取從系統啟動到當前的時間
* std::chrono::system_clock,受系統時間影響,可能會被修改
*
* @return 返回當前時間的時間戳(以毫秒為單位)
*/
static inline time_t GetTick()                  
{return chrono::duration_cast<chrono::milliseconds>(chrono::steady_clock::now().time_since_epoch()).count();   
}

4、添加定時器

/**
* @brief 添加定時器
*
* 將一個定時器節點添加到定時器列表中,并返回該節點的標識。
*
* @param msec 定時器超時時間,單位為毫秒
* @param cb 定時器超時時執行的回調函數
*
* @return 返回定時器的標識,類型為 TimerNode_S
*/
TimerNode_S AddTimer(int msec, TimerNode::Callback cb)
{time_t expire = GetTick() + msec;       //過期時間if(timeouts.empty() || expire <= timeouts.crbegin()->expire){auto pairs = timeouts.emplace(GetID(), expire, move(cb));return static_cast<TimerNode_S>(*pairs.first);}auto ele = timeouts.emplace_hint(timeouts.crbegin().base(), GetID(), expire, move(cb));return static_cast<TimerNode_S>(*ele);
}

5、刪除定時器

/**
* @brief 刪除定時器節點
*
* 從定時器集合中刪除指定的定時器節點。
*
* @param node 需要刪除的定時器節點
*/
void DelTimer(TimerNode_S& node)
{auto iter = timeouts.find(node);if(iter != timeouts.end()){timeouts.erase(iter);}
}

6、處理定時器任務

/**
* @brief 處理超時事件
*
* 該函數遍歷超時事件列表,對于已超時的每個事件,調用其回調函數進行處理,并從列表中移除該事件。
*
* @param now 當前時間戳
*/
void HandleTimeout(time_t now)
{auto iter = timeouts.begin();while(iter != timeouts.end() && iter->expire <= now){iter->cb(*iter);iter = timeouts.erase(iter);}
}struct epoll_event evs[64] = {0};
while(true){int n = epoll_wait(epfd, evs, 64, timer->TimeOut());time_t now = CTimer::GetTick();for(int j = 0; j < n; ++j){cout<<"epoll_wait:"<<endl;}timer->HandleTimeout(now);
}

在這里插入圖片描述

以上是采用I/O多路復用的最后一個超時參數,接下來更換成timerfd

7、主要調用的函數

/*
*功能:創建定時器
*clockfd: CLOCK_REALTIME-系統實時時鐘,與系統時間同步,受用戶手動修改時間影響。CLOCK_MONOTONIC-單調遞增時鐘,自系統啟動以來的時間,不受系統時間調整的影響
*flags:TFD_NONBLOCK(非阻塞模式)和 TFD_CLOEXEC(在 exec 調用時自動關閉文件描述符)          
*
*/
timerfd_create(int clockfd, int flags);/*
* 功能:用于設置定時器的初始超時時間和后續周期時間
* fd:timerfd
* flags: 控制定時器行為的標志:
*       0-------絕對時間
*       TFD_TIMER_ABSTIME------表示相對時間
* new_value: 指定了定時器的初始超時時間和(可選的)后續周期時間
* old_value: 用于恢復定時器到之前的狀態,常設為nullptr
*/
int timerfd_settime(int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value);

8、將之前的計算觸發時間的方式改成timerfd

void UpdateTimerfd(const int fd) {struct timespec abstime;auto iter = timeouts.begin();if (iter != timeouts.end()) {abstime.tv_sec = iter->expire / 1000;abstime.tv_nsec = (iter->expire % 1000) * 1000000;} else {abstime.tv_sec = 0;abstime.tv_nsec = 0;}struct itimerspec its = {.it_interval = {},.it_value = abstime};timerfd_settime(fd, TFD_TIMER_ABSTIME, &its, nullptr);
}int tfd = timerfd_create(CLOCK_MONOTONIC, 0);struct epoll_event evs[64] = {0};
while(true){timer->UpdateTimerfd(tfd);int n = epoll_wait(epfd, evs, 64, -1);time_t now = CTimer::GetTick();for(int j = 0; j < n; ++j){cout<<"epoll_wait:"<<endl;}timer->HandleTimeout(now);
}

在這里插入圖片描述

三、總結

  • 定時器在程序中無處不在,無論是硬件,還是網頁,客戶端,服務端等。
  • 在服務端上合理地使用定時器,能提高服務器的效率和穩定性,如定時清理緩存,在服務器高負載情況下,自動執行一些重要的任務等。
  • 定時器的數據結構多種多樣,有根據觸發時間排序的紅黑樹,最小堆,也有根據執行順序的時間輪。
  • 服務端常與網絡模塊協同處理
  • 服務端常基于事件驅動業務開展
  • 服務端除了協同網絡處理,復用系統調用

代碼:
Code
0voice·Github

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

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

相關文章

Mac版本Android Studio配置LeetCode插件

第一步&#xff1a;Android Studio里面找到Settings&#xff0c;找到Plugins&#xff0c;在Marketplace里面搜索LeetCode Editor。 第二步&#xff1a;安裝對應插件&#xff0c;并在Tools->LeetCode Plugin頁面輸入帳號和密碼。 理論上&#xff0c;應該就可以使用了。但是&a…

【ISP算法精粹】動手實戰:用 Python 實現 Bayer 圖像的黑電平校正

在數字成像領域&#xff0c;圖像信號處理器&#xff08;ISP&#xff09;如同幕后英雄&#xff0c;默默將傳感器捕獲的原始數據轉化為精美的圖像。而黑電平校正&#xff0c;作為ISP預處理流程中的關鍵一環&#xff0c;直接影響著最終圖像的質量。今天&#xff0c;我們就通過Pyth…

Oracle OCP與MySQL OCP認證如何選?

認證本質與定位差異 Oracle OCP Oracle OCP是Oracle公司推出的旗艦級數據庫專家認證&#xff0c;專注于其核心的閉源商業數據庫技術體系。核心領域包括RAC&#xff08;Real Application Clusters&#xff09;高可用集群、Data Guard容災解決方案、Exadata數據庫一體機集成以及…

MVVM、MVC的區別、什么是MVVM

一、什么是MVVM &#xff08;一&#xff09;定義 MVVM是Model - View - ViewModel的縮寫&#xff0c;它是一種軟件架構設計模式&#xff0c;主要用于構建用戶界面。這種模式將應用程序分為三個主要部分&#xff1a; Model&#xff08;模型層&#xff09; 它是應用程序中負責…

【SpringCache 提供的一套基于注解的緩存抽象機制】

Spring 緩存&#xff08;Spring Cache&#xff09;是 Spring 提供的一套基于注解的緩存抽象機制&#xff0c;常用于提升系統性能、減少重復查詢數據庫或接口調用。 ? 一、基本原理 Spring Cache 通過對方法的返回結果進行緩存&#xff0c;后續相同參數的調用將直接從緩存中讀…

HRI-2025 | 大模型驅動的個性化可解釋機器人人機交互研究

作者&#xff1a;Ferran Gebelli 1 ^{1} 1, Lavinia Hriscu 2 ^{2} 2, Raquel Ros 1 ^{1} 1, Sverin Lemaignan 1 ^{1} 1, Alberto Sanfeliu 2 ^{2} 2, Anais Garrell 2 ^{2} 2單位&#xff1a; 1 ^{1} 1PAL Robotics&#xff0c; 2 ^{2} 2IRI (UPC-CSIC)論文標題&#xff1a;P…

Gitee Wiki:重塑關鍵領域軟件研發的知識管理范式

在數字化轉型浪潮席卷全球的當下&#xff0c;關鍵領域軟件研發正面臨前所未有的知識管理挑戰。傳統文檔管理模式的局限性日益凸顯&#xff0c;知識傳承的斷層問題愈發嚴重&#xff0c;團隊協作效率的瓶頸亟待突破。Gitee Wiki作為新一代知識管理平臺&#xff0c;正在通過技術創…

JVM 內存溢出 詳解

內存溢出 內存溢出指的是內存中某一塊區域的使用量超過了允許使用的最大值&#xff0c;從而使用內存時因空間不足而失敗&#xff0c;虛擬機一般會拋出指定的錯誤。 在Java虛擬機中&#xff0c;只有程序計數器不會出現內存溢出的情況&#xff0c;因為每個線程的程序計數器只保…

dvwa8——SQL Injection(Blind)

由題目得這一關用盲注寫 LOW: 先用bp抓包一下 , 看到這low是get提交 , f12打開hackbar 輸入?id1時報錯 嘗試閉合 , 回顯正常 開始注入 1.order by 判斷列數,3的時候開始回顯報錯,所以有兩列 ?id1 order by 2--&SubmitSubmit# 2.無回顯位置可以爆出,我們通過盲注來繼…

探索分布式存儲與通信:去中心化共享及通訊(DSAC)

在當今數字化時代&#xff0c;分布式系統的重要性愈發凸顯。它不僅能提升數據的存儲安全性和可靠性&#xff0c;還能增強通信的效率和隱私性。于是我做了這個去中心化共享及通訊的程序&#xff0c;它構建了一個強大的分布式存儲和通信網絡&#xff0c;下面我們就來詳細了解其實…

ass字幕嵌入mp4帶偏移

# 格式轉化文件&#xff0c;包含多種文件的互相轉化&#xff0c;主要與視頻相關 from pathlib import Path import subprocess import random import os import reclass Utils(object):staticmethoddef get_decimal_part(x: float) -> float:s format(x, .15f) # 格式化為…

05 APP 自動化- Appium 單點觸控 多點觸控

文章目錄 一、單點觸控查看指針的指針位置實現手勢密碼&#xff1a; 二、多點觸控 一、單點觸控 查看指針的指針位置 方便查看手勢密碼-九宮格每個點的坐標 實現手勢密碼&#xff1a; 執行手勢操作&#xff1a; 按壓起點 -> 移動到下一點 -> 依次移動 -> 釋放&am…

【軟件】在 macOS 上安裝 MySQL

在 macOS 上安裝 MySQL 有多種方法&#xff0c;以下是兩種常見的安裝方式&#xff1a;通過 Homebrew 安裝和通過安裝包安裝。以下是詳細的步驟&#xff1a; 一、通過 Homebrew 安裝 MySQL Homebrew 是 macOS 的包管理器&#xff0c;使用它安裝 MySQL 非常方便。 1.安裝 Home…

第11節 Node.js 模塊系統

為了讓Node.js的文件可以相互調用&#xff0c;Node.js提供了一個簡單的模塊系統。 模塊是Node.js 應用程序的基本組成部分&#xff0c;文件和模塊是一一對應的。換言之&#xff0c;一個 Node.js 文件就是一個模塊&#xff0c;這個文件可能是JavaScript 代碼、JSON 或者編譯過的…

力扣熱題100之二叉樹的直徑

題目 給你一棵二叉樹的根節點&#xff0c;返回該樹的 直徑 。 二叉樹的 直徑 是指樹中任意兩個節點之間最長路徑的 長度 。這條路徑可能經過也可能不經過根節點 root 。 兩節點之間路徑的 長度 由它們之間邊數表示。 代碼 方法&#xff1a;遞歸 計算二叉樹的直徑可以理解…

OpenCV CUDA模塊圖像處理------創建CUDA加速的Canny邊緣檢測器對象createCannyEdgeDetector()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 該函數用于創建一個 CUDA 加速的 Canny 邊緣檢測器對象&#xff08;CannyEdgeDetector&#xff09;&#xff0c;可以在 GPU 上高效執行 Canny 邊…

unix/linux,sudo,其內部結構機制

我們現在深入sudo的“引擎室”,探究其內部的結構和運作機制。這就像我們從觀察行星運動,到深入研究萬有引力定律的數學表達和物理內涵一樣,是理解事物本質的關鍵一步。 sudo 的內部結構與機制詳解 sudo 的執行流程可以看作是一系列精心設計的步驟,確保了授權的準確性和安…

什么是 TOML?

&#x1f6e0; Rust 配置文件實戰&#xff1a;TOML 語法詳解與結構體映射&#xff08; 在 Rust 中&#xff0c;Cargo.toml 是每個項目的心臟。它不僅定義了項目的名稱、版本和依賴項&#xff0c;還使用了一種輕巧易讀的配置語言&#xff1a;TOML。 本文將深入解析 TOML 的語法…

react native webview加載本地HTML,解決iOS無法加載成功問題

在react native中使用 “react-native-webview”: “^13.13.5”,加載HTML文件 Android: 將HTML文件放置到android/src/main/assets目錄&#xff0c;訪問 {uri: file:///android_asset/markmap/index.html}ios: 在IOS中可以直接可以直接放在react native項目下&#xff0c;訪問…

數據結構(JAVA版)練習題

&#xff08;題目難易程度與題號順序無關哦&#xff09; 目錄 1、多關鍵字排序 2、集合類的綜合應用問題 3、數組排序 4、球的相關計算問題 5、利用類對象計算日期 6、日期計算問題 7、星期日期的計算 8、計算坐標平面上兩點距離 9、異常處理設計問題 10、Java源文件…