嵌入式C/C++按特定速率调用函数:最佳实践、示例及跨平台实现问询
嵌入式C/C++按特定速率调用函数的最佳实践与示例
核心原则
- 拒绝忙等循环:纯delay会浪费CPU资源,尤其在Jetson和笔记本这类带OS的平台上完全没必要
- 依赖系统时钟/定时器:优先用平台提供的成熟定时器API,避免自己造轮子
- 控制任务执行时长:任务执行时间必须远小于调度周期(比如100ms周期的任务,执行时间要压在几ms内),否则会拖垮整个调度节奏
- 跨平台一致性:用统一的单调时间基准,屏蔽底层平台差异
分平台实现方案
1. Jetson-Nano(Linux平台)
Linux下有两种主流方案,按需选择:
- 线程+clock_nanosleep:新手友好,逻辑简单,用线程循环等待到指定时间再执行任务
- POSIX定时器:精度更高,支持信号触发或线程回调,适合对精度要求苛刻的场景
- 可选优化:将调度线程设为实时优先级,减少Linux调度器的延迟
2. 笔记本(跨Windows/Linux)
直接用C++11及以上标准库的<chrono>和<thread>组件,实现跨平台通用的调度逻辑,保证Jetson和笔记本上的执行速率完全一致。
最优推荐方案(新手友好+跨平台)
优先选择C++标准库实现的线程+绝对时间等待,既跨平台,又能避免累计误差,代码易读易维护。如果Jetson需要更高精度,再切换到POSIX定时器。
跨平台通用示例代码
#include <iostream> #include <thread> #include <chrono> #include <atomic> #include <mutex> // 原子变量控制调度启停 std::atomic<bool> g_running(true); // 互斥锁保护控制台输出(避免多线程打印混乱) std::mutex g_print_mutex; // 50ms周期任务 void task_50ms() { static int count = 0; std::lock_guard<std::mutex> lock(g_print_mutex); std::cout << "50ms任务执行,次数:" << ++count << std::endl; } // 100ms周期任务 void task_100ms() { static int count = 0; std::lock_guard<std::mutex> lock(g_print_mutex); std::cout << "100ms任务执行,次数:" << ++count << std::endl; } // 通用调度模板函数 template<typename Func> void schedule_task(Func task, std::chrono::milliseconds period) { using namespace std::chrono; auto next_run_time = steady_clock::now() + period; while (g_running) { // 等待到下一次执行的绝对时间(避免累计误差) std::this_thread::sleep_until(next_run_time); // 执行任务 task(); // 更新下一次执行时间 next_run_time += period; } } int main() { // 启动两个调度线程 std::thread t1(schedule_task<decltype(task_50ms)>, task_50ms, 50ms); std::thread t2(schedule_task<decltype(task_100ms)>, task_100ms, 100ms); // 运行10秒后停止 std::this_thread::sleep_for(10s); g_running = false; // 等待线程结束 t1.join(); t2.join(); return 0; }
Jetson-Nano高精度优化示例(POSIX定时器)
如果需要微秒级精度,可使用Linux专属的POSIX定时器:
#include <iostream> #include <signal.h> #include <time.h> #include <atomic> #include <mutex> std::atomic<bool> g_running(true); std::mutex g_print_mutex; void task_100ms(union sigval arg) { static int count = 0; std::lock_guard<std::mutex> lock(g_print_mutex); std::cout << "100ms任务执行(POSIX定时器),次数:" << ++count << std::endl; } int main() { timer_t timer_id; struct sigevent sev; struct itimerspec its; // 设置线程回调模式 sev.sigev_notify = SIGEV_THREAD; sev.sigev_notify_function = task_100ms; sev.sigev_notify_attributes = nullptr; // 创建基于单调时钟的定时器 if (timer_create(CLOCK_MONOTONIC, &sev, &timer_id) == -1) { perror("timer_create失败"); return 1; } // 设置定时器参数:首次100ms后执行,之后每100ms执行一次 its.it_value.tv_sec = 0; its.it_value.tv_nsec = 100 * 1000 * 1000; // 100ms its.it_interval = its.it_value; // 启动定时器 if (timer_settime(timer_id, 0, &its, nullptr) == -1) { perror("timer_settime失败"); return 1; } // 运行10秒后停止 sleep(10); g_running = false; timer_delete(timer_id); return 0; }
关键注意事项
- 用
steady_clock(单调时钟)而非system_clock:避免系统时间调整导致的调度异常 - 线程安全:多线程调度时,共享资源必须用互斥锁保护
- 任务超时处理:如果任务执行时间超过周期,要优化任务逻辑或拆分任务,避免调度延迟累积
内容的提问来源于stack exchange,提问作者crose72
相关产品推荐
相关产品推荐

