使用 eventpp 構建跨 RT-Thread 與 ARM-Linux 的輕量級 Active Object(AO)事件驅動框架

0. 引言

本文展示一個實踐路徑:以輕量級 C++ 事件庫 eventpp 為核心,設計并實現一個面向嵌入式的、可移植的 Active Object(AO)事件驅動架構。該架構滿足以下目標:

  • 跨平臺兼容:單套代碼在 RT-Thread(或裸機)與 ARM-Linux 下均可編譯與運行
  • 開源免費:使用 eventpp 與自研輕量庫,避免商業授權成本
  • 可定制:通過策略(Policy)層注入不同的鎖、容器、優先級實現適配不同平臺

1. 為什么選擇 eventpp

eventpp 是一個純頭文件的現代 C++ 事件庫,特點非常契合嵌入式與資源受限環境的需求:

  • 純頭文件、無運行時依賴,易于移植到 RT-Thread、裸機或交叉編譯的 ARM-Linux
  • 提供 CallbackList、EventDispatcher、EventQueue 三大功能模塊,能滿足同步與異步事件場景
  • 支持 Policy 注入,可替換底層容器、鎖、優先級策略 — 便于將內存/并發策略綁定到平臺要求
  • 小巧、可讀、易于裁剪:便于做靜態分配或替換動態容器

總結:eventpp 不是一個完整的 RTOS 或高級狀態機框架,但作為“事件發布/訂閱 + 異步隊列”的基石非常合適。

2. eventpp 三大核心組件速覽

  1. CallbackList

    • 基礎的回調列表,可注冊任意可調用對象(函數、Lambda、成員函數),在回調執行過程中可安全添加/刪除。適合“固定事件、原型各異”的簡單場景。
  2. EventDispatcher

    • 類型到回調列表的映射:同步按事件類型分發,所有監聽器立刻執行。支持自定義 Policy,從復雜事件對象中抽取事件類型。
  3. EventQueue

    • 異步版 EventDispatcher:先 enqueue 入隊,再 process 批量分發。支持跨線程 wait()/process(),也可基于自定義 Policy 實現優先級調度。

這些組件可以組合成 AO(Active Object)模型:每個 AO 維護自己的 EventQueue(或多個隊列),獨立線程消費事件并驅動狀態機或回調。

3. 使用速覽(示例)

3.1 CallbackList 簡單示例

#include <eventpp/callbacklist.h>eventpp::CallbackList<void(int)> cbList;auto h1 = cbList.append([](int x){ printf("A: %d\n", x); });
auto h2 = cbList.append([](int x){ printf("B: %d\n", x); });cbList(42);   // 輸出 A: 42  B: 42cbList.remove(h2);
cbList(7);    // 只輸出 A: 7

3.2 EventDispatcher 基礎用法

#include <eventpp/eventdispatcher.h>enum class Sig { Start, Stop };eventpp::EventDispatcher<Sig, void(int), std::map> dispatcher;dispatcher.appendListener(Sig::Start, [](int v){ printf("Start %d\n", v); });dispatcher.dispatch(Sig::Start, 123);  // 輸出 Start 123

3.3 EventQueue 異步隊列

#include <eventpp/eventqueue.h>eventpp::EventQueue<std::string> queue;queue.appendListener([](const std::string &s){printf("Got: %s\n", s.c_str());
});queue.enqueue("Hello");
queue.enqueue("World");queue.process();  // 輸出 Got: Hello  Got: World

4. 內存策略:靜態分配與零動態分配

在 RT-Thread 或硬實時路徑中要保證可測的 WCET 與避免內存抖動,應優先使用靜態或預分配結構存放事件。以下給出兩種常見策略與實現樣例。

  • 靜態環形緩沖(單生產者單消費者 / 多生產者場景需額外鎖或 lock-free 結構)
  • 對象池(預分配固定數量事件對象,支持復用)
  • 可控的少量動態內存(僅在初始化階段分配)在資源非常受限時也可接受

靜態環形緩沖示例(已經在問題中給出,這里補充線程安全注意):

// StaticRing.h
#include <atomic>
#include <cstddef>template<typename T, size_t N>
class StaticRing {static_assert(N >= 2, "N must be >= 2");T buffer[N];std::atomic<size_t> head{0}, tail{0};
public:bool enqueue(const T &v) {size_t t = tail.load(std::memory_order_relaxed);size_t next = (t + 1) % N;if(next == head.load(std::memory_order_acquire)) {return false; // 隊列已滿}buffer[t] = v;tail.store(next, std::memory_order_release);return true;}bool dequeue(T &out) {size_t h = head.load(std::memory_order_relaxed);if(h == tail.load(std::memory_order_acquire)) {return false; // 隊列為空}out = buffer[h];head.store((h + 1) % N, std::memory_order_release);return true;}
};

注意:

  • 對于多生產者/多消費者,需要額外的原子或鎖保護(或使用專門的 lock-free 隊列實現)
  • 內存拷貝成本:如果事件對象較大,建議使用小事件句柄(ID + 指針到對象池)或移動語義
  • 避免在 ISR 中進行占用長時間的操作:在 ISR 中只做入隊與喚醒,處理留給 AO 線程

5. 平臺抽象層(PAL):解耦 RTOS / Linux 實現

為了實現同一套 AO 代碼在 RT-Thread 和 ARM-Linux 下工作,推薦引入一個 PAL(Platform Abstraction Layer)最小 API:

  • 線程 / 任務創建:PalThread::create(…)
  • 互斥鎖 / 遞歸鎖:PalMutex / PalRecursiveMutex
  • 信號量 / 事件:PalSemaphore
  • 中斷安全入隊的 primitive(如果 RTOS 提供 ISR-safe API,可包裝)
  • 時間與延時:PalTime::sleepMs, now

示例接口(偽頭文件):

// pal.h (偽接口)
#pragma once
#include <functional>
#include <cstdint>namespace pal {using ThreadFunc = std::function<void()>;struct ThreadHandle { /* opaque */ };class Thread {
public:static ThreadHandle create(const char* name, ThreadFunc func, int priority, size_t stackSize = 4096);static void join(ThreadHandle);// ...
};class Mutex {
public:Mutex();void lock();bool try_lock();void unlock();
};class Semaphore {
public:Semaphore(unsigned initial = 0);void acquire();bool try_acquire();void release();
};} // namespace pal

在 RT-Thread 下實現這些接口時要注意 ISR-safe API(例如 rt_sem_release_from_isr);在 Linux 下用 pthreads 或 std::thread/std::mutex 實現。

6. EventQueue 的策略注入(Policy)與 AO 模型實現

利用 eventpp 的 Policy 注入機制,我們可以為不同平臺定制底層鎖、容器和優先級策略,例如把靜態環形緩沖注入到 EventQueue。

示例 Policy 定義:

// MyPolicies.h
#include <eventpp/eventqueue.h>
// 假設已包含 StaticRing<Event> 和平臺 PalMutexusing RtStaticPolicy = eventpp::EventQueuePolicy</*ContainerBuilder*/ eventpp::policy::VectorLikeBasedContainer<StaticRingWrapper>,/*Lock*/ PalMutex,/*PriorityPolicy*/ eventpp::DefaultPriorityPolicy
>;// 使用示例(偽代碼,視具體 eventpp 版本接口而定)
using AoEventQueueRt = eventpp::EventQueue<Event, void(const Event&), RtStaticPolicy>;

Active Object 的實現偽代碼如下(補充完整細節):

class ActiveObject {
public:ActiveObject(const char* name): running(true){threadHandle = pal::Thread::create(name, [this](){ run(); }, /*priority=*/10, /*stack=*/4096);}~ActiveObject() {running = false;eventSem.release(); // wake up to exitpal::Thread::join(threadHandle);}// 普通上下文發事件bool post(const Event &e) {if(eventQueue.enqueue(e)) {eventSem.release();return true;} else {++stats.dropped;return false;}}// ISR 中調用(必須使用 ISR-safe enqueue 與 notify)bool postFromIsr(const Event &e) {if(eventQueue.enqueueFromIsr(e)) { // 需要容器/策略支持isrFlag.store(true, std::memory_order_release);// 使用 ISR-safe 喚醒eventSem.releaseFromIsr();return true;} else {++stats.dropped;return false;}}private:void run() {while(running) {eventSem.acquire();// 處理直到隊列為空或處理批量eventQueue.process(); }}AoEventQueueRt      eventQueue;pal::Semaphore      eventSem;std::atomic<bool>   isrFlag{false};std::atomic<bool>   running{true};pal::ThreadHandle   threadHandle;// 統計、狀態機、回調列表等
};

要點:

  • eventQueue.enqueueFromIsr 與 Semaphore::releaseFromIsr 的可用性取決于具體 PAL 與容器實現
  • 在 RTOS/裸機路徑,確保 ISR 中的操作為最小耗時且可中斷安全
  • AO 的 run() 中應盡量避免長阻塞(除非這是設計意圖),可在處理每個事件時記錄處理時間用于 WCET 測量

7. ISR 與 AO 協作:從中斷安全到喚醒機制

設計 AO+ISR 協作時的典型模式:

  1. 在 ISR 中構建或引用事件(盡量小),調用 ISR-safe enqueue(或寫入環形緩沖直接內存寫入)
  2. 在 ISR 中僅進行必要的喚醒(如給信號量/事件標志),避免調用復雜的調度邏輯
  3. AO 線程被喚醒后逐條或批量處理事件并執行較長/不安全的操作(比如動態內存、文件操作等)

注意事項:

  • 事件對象的內存管理:ISR 中最好只寫入小而固定大小的數據或索引到對象池,避免在 ISR 中 new/delete
  • 優先級反轉:如果 AO 與 ISR 之間有鎖競爭,需防止優先級反轉,使用 RTOS 提供的優先級繼承或選擇無鎖方案
  • 批處理以減少上下文切換:在 AO 中 process() 可以一次處理 N 條事件,或者處理直到隊列為空,平衡延遲與吞吐

8. 優先級、調度與避免饑餓(Priority Policy)

如果系統包含高/中/低優先級事件,需要在 EventQueue 層支持優先級:

常見方案:

  • 多隊列(per-priority queue):高優先級隊列先處理,低優先級隊列后處理,可防止高頻低優先任務饑餓低優先任務(通過令牌/輪詢策略)
  • 單隊列帶優先排序:插入時用比較器排列,缺點是插入復雜度高且插隊可能破壞 WCET 可測性
  • 混合:固定優先級數目的環形緩沖數組 + 限額處理策略(限制連續處理高優先事件的數量)

示例:多隊列 + 輪詢限額(偽代碼)

void process() {int highCount = 0;while(true) {if(dequeueFromHighQueue(event)) {handle(event);++highCount;if(highCount >= HIGH_LIMIT) {// 讓出一次機會處理中/低優先if(dequeueFromMidQueue(event)) { handle(event); }if(dequeueFromLowQueue(event)) { handle(event); }highCount = 0;}continue;}if(dequeueFromMidQueue(event)) { handle(event); continue; }if(dequeueFromLowQueue(event)) { handle(event); continue; }break;}
}

要點:

  • WCET:引入優先級后必須對最壞情況執行路徑重新評估
  • 可測性優先:在硬實時場景下選擇更可控(固定時間限制/批量上限)的策略

9. 部署示例:RT-Thread 與 ARM-Linux 的實現要點

RT-Thread 實現注意點:

  • 使用 rt_thread_create、rt_sem_take/release、rt_mutex_* 等替代 PAL 接口
  • ISR 中使用 rt_sem_release_fromISR(或 rt_sem_release + rt_hw_interrupt_mask/unmask)
  • 在 bsp 層做好堆棧、內存池的靜態分配,避免動態分配(new/malloc)

ARM-Linux 實現注意點:

  • 使用 std::thread / pthreads / std::mutex / std::condition_variable 或者基于 epoll 的事件循環
  • 如果需要硬實時級別,可使用 PREEMPT_RT 或基于 rtprio 的實時進程來運行 AO 線程
  • 內存策略:在進程初始化時使用 malloc 大對象池,運行時避免再分配

兩端共享代碼實踐:

  • 把核心 AO、eventpp 使用、狀態機邏輯放入可編譯在兩端的庫(僅依賴 STL 或做條件編譯)
  • PAL 在不同平臺實現不同文件,通過 cmake 或 makefile 在交叉編譯時選擇

示例目錄結構(建議):

  • src/core/ (AO、事件、狀態機、policy glue)
  • src/pal/rtthread/ (RT-Thread 的 PAL 實現)
  • src/pal/linux/ (Linux 的 PAL 實現)
  • examples/ (運行示例)
  • tests/ (單元與集成測試)

10. 示例架構圖

時序圖

ISRPALEventQueueAOCBpostFromIsr(e)enqueue ISR-safesignal / wakeupprocess()dispatch callbacksISRPALEventQueueAOCB

類關系

ActiveObject
+post(Event)
+postFromIsr(Event)
-run()
EventQueue
PalThread
PalMutex

優先級多隊列示意

ISR Producers
high
mid
low
process
QH
ISR1
QM
ISR2
QL
ThreadProd
Active Object
Handler

11. 與其它框架的對比與權衡

  • QP/C++(付費)

    • 優點:成熟的 AO 框架、事件池、狀態機支持、面向嵌入式的設計、硬實時適配能力強
    • 缺點:授權成本、學習曲線、集成與裁剪成本
  • eventpp + 自研 PAL + 對象池(本文方案)

    • 優點:零成本、極簡、可裁剪、跨平臺、可控內存分配
    • 缺點:需要自行完成對象池、狀態機、嚴格的實時保障需要手工設計

選擇要點:

  • 如果團隊需要商用支持、成熟工具鏈與硬實時保障,QP/C++ 更合適
  • 如果希望快速上手、跨平臺且避免授權成本,eventpp+自研方案更靈活

12. 總結與建議

本文給出一種基于 eventpp 的輕量級 AO 模式實踐,適用于對可移植性與內存可控性有較高要求的嵌入式項目。關鍵建議:

  • 核心事件分發邏輯使用 eventpp,其 policy 注入能力讓跨平臺實現更簡單
  • 在 RTOS/裸機路徑優先使用靜態/預分配結構,避免中斷與任務中動態分配
  • 設計 PAL,隔離平臺差異,保持核心邏輯可復用
  • 對優先級、饑餓與 WCET 做專門測試與測量,并在設計中加上容錯策略(如丟棄策略、統計報警)
  • 對性能要求極高或強實時約束的場景,慎重評估是否需要更底層(內核級)支持或采用成熟商業框架

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

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

相關文章

【python實用小腳本-193】Python全能PDF小助手:剪切/合并/旋轉/加密一條龍——再也不用開會員

Python全能PDF小助手&#xff1a;剪切/合并/旋轉/加密一條龍——再也不用開會員 PDF編輯, 本地處理, 零會員費, 多功能腳本, 瑞士軍刀 故事開場&#xff1a;一把瑞士軍刀救了周五下班的你 周五 17:55&#xff0c;老板甩來一堆 PDF&#xff1a; “把第 3、7 頁刪掉”“再和合同合…

Ubuntu根分區擴容

目錄 1.先查看/dev/sda 整塊磁盤設備的分區占用情況&#xff1a; 2.在VMware中編輯虛擬機&#xff1a; 3.進入虛擬機&#xff0c;進入disk應用程序&#xff1a; 4.擴容文件系統 5.最后通過df-h lsblk或通過可視化GParted進行驗證。 1.先查看/dev/sda 整塊磁盤設備的分區占…

智慧城市SaaS平臺/市政設施運行監測系統之空氣質量監測系統、VOC氣體監測系統、污水水質監測系統及環衛車輛定位調度系統架構內容

1. 空氣質量監測系統1) 監測點管理 a) 監測點基本信息 支持記錄空氣質量監測點的名稱、位置、類型、設備配置等信息。 b) 監測點分布地圖 支持通過GIS地圖展示監測點的分布情況&#xff0c;支持地圖查詢和導航。 2) 空氣質量監測 a) 實時數據采集 支持實時采集空氣質量數據&…

PiscCode迅速集成YOLO-Pose 實現姿態關鍵點軌跡跟蹤應用

在計算機視覺領域&#xff0c;人體姿態檢測與軌跡跟蹤是很多應用場景的核心技術&#xff0c;例如運動分析、行為識別、智能監控等。本文將介紹如何在 PiscCode 平臺上&#xff0c;利用 YOLO-Pose 模型進行姿態估計&#xff0c;并實現多人關鍵點軌跡跟蹤。 一、什么是 PiscCode …

HTTP的狀態碼有哪些,并用例子說明一下

問題HTTP的狀態碼有哪些&#xff0c;并用例子說明一下我的回答HTTP狀態碼是服務器對客戶端請求的響應碼&#xff0c;它們按照不同的功能被分為五大類。我來介紹一下主要的狀態碼及其實際應用場景&#xff1a;1xx&#xff08;信息性狀態碼&#xff09;&#xff1a;表示請求已接收…

【51單片機】【protues仿真】基于51單片機寵物投食器系統

目錄 一、主要功能 二、使用步驟 三、硬件資源 四、軟件設計 五、實驗現象 一、主要功能 1、LCD1602液晶顯示當前時間 2、按鍵設置時間&#xff0c;5個定時投喂時間? 3、可以通過手動按鍵進行投喂食物 4、步進電機模擬投喂食物 二、使用步驟 基于51單片機的寵物自動投…

掌握設計模式--命令模式

命令模式&#xff08;Command Pattern&#xff09; 命令模式&#xff08;Command Pattern&#xff09;是一種行為型設計模式&#xff0c;它將請求&#xff08;命令&#xff09;封裝成對象&#xff0c;從而使您能夠參數化客戶端&#xff08;調用者&#xff09;使用不同的請求、…

STM32之beep、多文件、延遲、按鍵以及呼吸燈

一、Beep控制 原理圖分析&#xff1a; 蜂鳴器三極管控制引腳對應 MCU PB8。當前蜂鳴器對應的電路中&#xff0c;三極管是 NPN 三極管&#xff0c;當前【基極】存在小電流&#xff0c;當前三極管導通。要求對應 PB8 引腳對外輸出電壓 / 電流。當前 PB8 輸出高電平&#xff0c;當…

C++的struct里面可以放函數,討論一下C++和C關于struct的使用區別

我們來看一個C代碼下面的struct結構體: struct UserValue {float lx;float ly;float rx;float ry;float L2;// 【構造函數】UserValue() {setZero();}// 【成員函數】void setZero() {lx 0;ly 0;rx 0;ry 0;L2 0;} };在這篇文章中&#xff0c;我們將來詳細解釋一下為什么 U…

【Kubernetes知識點】資源配額與訪問控制

目錄 1.解釋ResourceQuota的作用。 2.解釋Service Account的用途。 3.詳細解釋Role和ClusterRole。 4.什么是K8s的NetworkPolicy&#xff1f; 5.詳細描述在K8s中如何控制跨Namespace的Pod訪問&#xff1f; 1.解釋ResourceQuota的作用。 ResourceQuota&#xff08;資源配額…

在SAP Query中添加雙擊事件

在SAP系統中&#xff0c;SAP Query是一個強大的工具&#xff0c;允許用戶自定義報告以滿足特定的數據查詢需求。它提供了靈活的報表設計功能&#xff0c;使非編程背景的用戶也能創建和修改查詢。在某些情況下&#xff0c;我們可能希望在查詢結果上添加交互性&#xff0c;比如通…

c++:MFC中sqlite3的使用(附實際案例)

MFC中sqlite3的使用sqlite3介紹sqlite3安裝常用API函數操作流程接口函數執行sql語句函數回調函數MFC中案例實踐控制臺實踐sqlite3介紹 SQLite 是一個軟件庫&#xff0c;實現了自給自足的、無服務器的、零配置的、事務性的 SQL 數據庫引擎。SQLite 是在世界上最廣泛部署的 SQL …

LeetCode第1019題 - 鏈表中的下一個更大節點

題目 解答 class Solution {Stack<Integer> stack new Stack<>();List<Integer> values new LinkedList<>();public int[] nextLargerNodes(ListNode head) {nextLargerNodes2(head);return values.stream().mapToInt(x -> x).toArray();}publi…

STM32 硬件I2C讀寫MPU6050

本文代碼基于 STM32 單片機&#xff0c;通過 I2C 總線驅動 MPU6050 六軸傳感器&#xff08;集成加速度計與陀螺儀&#xff09;&#xff0c;實現傳感器初始化、ID 讀取、原始數據采集&#xff0c;并借助 OLED 顯示屏實時展示加速度&#xff08;AccX、AccY、AccZ&#xff09;與角…

倍福下的EC-A10020-P2-24電機調試說明

今天調試EC-A10020-P2-24電機&#xff0c;采用力位混合控制指令進行控制&#xff0c;無前饋力矩&#xff0c;只調節Kp和Kd,跟蹤紅色軌跡&#xff08;正弦信號&#xff1a;幅值10&#xff0c;頻率0.5Hz&#xff09;&#xff0c;結果顯示Kp 180, Kd 40&#xff0c;實際上Kp進一步…

SQL注入1----(sql注入原理)

一.前言前面我們講解了一下信息收集&#xff0c;本章節我們來講解一下sql注入的基本原理&#xff0c;我們拿之前搭建的測試網站pikachu來測試&#xff0c;對應工具包也已經放在了工具里面&#xff0c;大家可以自行去下載。SQL注入攻擊漏洞的原因&#xff0c;是由于程序員在編寫…

C++智能指針詳解:用法與實踐指南

C智能指針詳解&#xff1a;用法與實踐指南 在C編程中&#xff0c;動態內存管理始終是開發者面臨的重要挑戰。手動分配和釋放內存不僅繁瑣&#xff0c;還容易因疏忽導致內存泄漏、懸垂指針等問題。為解決這些痛點&#xff0c;C標準庫引入了智能指針&#xff08;Smart Pointers&a…

fastdds qos:DurabilityQosPolicy

假如DataWriter先起來&#xff0c;并且已經寫了一些數據&#xff0c;之后有新的DataReader起來&#xff0c;那么新起來的DataReader能不能接收到它啟動之前&#xff0c;DataWriter發布的數據呢。DurabilityQosPolicy用來做這種控制。VOLATILE_DURABILITY_QOS&#xff1a;易失的…

【讀代碼】SQLBot:開源自然語言轉SQL智能助手原理與實踐

一、項目簡介 SQLBot 是 DataEase 團隊開源的自然語言轉 SQL 智能助手,致力于讓非技術用戶也能通過自然語言與數據庫對話,自動生成 SQL 查詢,實現自助數據分析、智能BI問答、報表生成等場景。SQLBot 結合了大語言模型(LLM)、數據庫元數據解析、SQL解析與執行等多項技術,…

開題報告被退回?用《基于大數據的慢性腎病數據可視化分析系統》的Hadoop技術,一次通過不是夢

&#x1f496;&#x1f496;作者&#xff1a;計算機編程小咖 &#x1f499;&#x1f499;個人簡介&#xff1a;曾長期從事計算機專業培訓教學&#xff0c;本人也熱愛上課教學&#xff0c;語言擅長Java、微信小程序、Python、Golang、安卓Android等&#xff0c;開發項目包括大數…