You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单个进程中实现多个同类型C++实时定时器回调?

我之前在做Linux和Android应用开发时刚好碰到过这个问题,setitimer的单定时器限制确实挺头疼的。针对你的需求——多实时超时任务、跨Ubuntu和Android,给你几个实用的解决方案:

方案1:手动维护定时器链表 + 单个setitimer模拟多任务

这是最基础的兼容方案,完全依赖系统原生的setitimer,不需要额外依赖,适合所有支持setitimer的系统(包括Ubuntu和全版本Android)。

核心思路是:

  • 自己维护一个按到期时间排序的任务链表,每个节点存任务的绝对到期时间、回调函数和参数
  • 只设置一个setitimer,超时时间设为链表中最早到期的任务时间
  • 每次定时器触发(收到SIGALRM信号)时,遍历链表执行所有到期的任务,然后更新setitimer为下一个最早到期的任务时间

关键注意点:

  1. 用绝对时间(比如clock_gettime(CLOCK_MONOTONIC))计算到期时间,避免累计误差
  2. 信号处理函数里别直接执行复杂回调(非异步安全函数会出问题),可以用管道通知主线程处理任务
  3. 链表操作要保证线程安全(如果是多线程环境)

简化代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

typedef struct TimerTask {
    struct timespec expire_time;
    void (*callback)(void*);
    void* arg;
    struct TimerTask* next;
} TimerTask;

static TimerTask* timer_list = NULL;
static int pipe_fd[2];

// 添加任务到有序链表
void add_timer_task(long long ms, void (*cb)(void*), void* arg) {
    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    TimerTask* new_task = malloc(sizeof(TimerTask));
    
    // 计算绝对到期时间
    new_task->expire_time.tv_sec = now.tv_sec + ms / 1000;
    long long nsec = now.tv_nsec + (ms % 1000) * 1000000;
    if (nsec >= 1000000000) {
        new_task->expire_time.tv_sec += 1;
        new_task->expire_time.tv_nsec = nsec - 1000000000;
    } else {
        new_task->expire_time.tv_nsec = nsec;
    }
    new_task->callback = cb;
    new_task->arg = arg;
    new_task->next = NULL;

    // 插入排序到链表(保证链表按到期时间升序)
    if (!timer_list || timespec_cmp(&new_task->expire_time, &timer_list->expire_time) < 0) {
        new_task->next = timer_list;
        timer_list = new_task;
    } else {
        TimerTask* curr = timer_list;
        while (curr->next && timespec_cmp(&new_task->expire_time, &curr->next->expire_time) >= 0) {
            curr = curr->next;
        }
        new_task->next = curr->next;
        curr->next = new_task;
    }

    // 更新setitimer
    update_setitimer();
}

// 更新setitimer为最早到期的任务时间
static void update_setitimer() {
    struct itimerval itv = {0};
    if (!timer_list) {
        setitimer(ITIMER_REAL, &itv, NULL);
        return;
    }

    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    long long diff_sec = timer_list->expire_time.tv_sec - now.tv_sec;
    long long diff_nsec = timer_list->expire_time.tv_nsec - now.tv_nsec;

    // 处理已经到期的情况
    if (diff_sec < 0 || (diff_sec == 0 && diff_nsec < 0)) {
        diff_sec = 0;
        diff_nsec = 0;
    }

    itv.it_value.tv_sec = diff_sec;
    itv.it_value.tv_usec = diff_nsec / 1000;
    setitimer(ITIMER_REAL, &itv, NULL);
}

// 信号处理函数:通过管道通知主线程处理任务
static void sigalrm_handler(int sig) {
    write(pipe_fd[1], "1", 1);
}

// 主线程处理到期任务
void handle_timer_tasks() {
    char buf[1];
    read(pipe_fd[0], buf, 1);

    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    TimerTask** curr = &timer_list;

    while (*curr) {
        TimerTask* task = *curr;
        if (timespec_cmp(&task->expire_time, &now) <= 0) {
            // 移除任务并执行回调
            *curr = task->next;
            task->callback(task->arg);
            free(task);
        } else {
            break; // 链表有序,后续任务未到期
        }
    }

    update_setitimer();
}

// 辅助函数:比较两个timespec
static int timespec_cmp(const struct timespec* a, const struct timespec* b) {
    if (a->tv_sec != b->tv_sec) return a->tv_sec - b->tv_sec;
    return a->tv_nsec - b->tv_nsec;
}

// 示例回调
void test_callback(void* arg) {
    printf("Task %d triggered!\n", *(int*)arg);
    free(arg);
}

int main() {
    pipe(pipe_fd);
    struct sigaction sa = {0};
    sa.sa_handler = sigalrm_handler;
    sigaction(SIGALRM, &sa, NULL);

    // 添加测试任务
    int* arg1 = malloc(sizeof(int)); *arg1 = 1;
    add_timer_task(2000, test_callback, arg1); // 2秒后触发
    int* arg2 = malloc(sizeof(int)); *arg2 = 2;
    add_timer_task(1000, test_callback, arg2); // 1秒后触发

    while (1) {
        handle_timer_tasks();
    }
    return 0;
}
方案2:使用POSIX定时器(timer_create/timer_settime)

如果你的目标Android版本在API 21及以上(现在主流设备都满足),POSIX定时器是更省心的选择——系统原生支持多个独立的实时定时器,不需要自己维护链表。

核心特性:

  • 每个定时器对应一个timer_t对象,独立管理
  • 支持两种通知方式:发送信号(可以是实时信号)、启动线程执行回调
  • 时间精度更高,支持纳秒级设置

简化代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>

typedef struct TimerArg {
    void (*callback)(void*);
    void* arg;
} TimerArg;

// 实时信号处理函数
static void timer_signal_handler(int sig, siginfo_t* si, void* uc) {
    TimerArg* arg = si->si_value.sival_ptr;
    arg->callback(arg->arg);
    free(arg);
}

// 创建一个单次触发的实时定时器
timer_t create_realtime_timer(long long ms, void (*cb)(void*), void* arg) {
    struct sigevent sev = {0};
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIGRTMIN; // 使用实时信号,避免冲突
    TimerArg* timer_arg = malloc(sizeof(TimerArg));
    timer_arg->callback = cb;
    timer_arg->arg = arg;
    sev.sigev_value.sival_ptr = timer_arg;

    // 设置信号处理函数
    struct sigaction sa = {0};
    sa.sa_sigaction = timer_signal_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGRTMIN, &sa, NULL);

    timer_t timerid;
    timer_create(CLOCK_REALTIME, &sev, &timerid);

    // 设置超时时间
    struct itimerspec its = {0};
    its.it_value.tv_sec = ms / 1000;
    its.it_value.tv_nsec = (ms % 1000) * 1000000;
    timer_settime(timerid, 0, &its, NULL);

    return timerid;
}

// 取消定时器
void cancel_timer(timer_t timerid) {
    timer_delete(timerid);
}

// 示例回调
void test_callback(void* arg) {
    printf("Task %d triggered!\n", *(int*)arg);
    free(arg);
}

int main() {
    int* arg1 = malloc(sizeof(int)); *arg1 = 1;
    timer_t t1 = create_realtime_timer(2000, test_callback, arg1);
    int* arg2 = malloc(sizeof(int)); *arg2 = 2;
    timer_t t2 = create_realtime_timer(1000, test_callback, arg2);

    // 等待任务触发
    pause();
    cancel_timer(t1);
    cancel_timer(t2);
    return 0;
}
方案3:使用事件驱动库(libevent/libuv)

如果你的项目本身是事件驱动架构,直接用成熟的事件库的定时器功能最省心——这些库已经封装好了多定时器的实现,处理了所有细节(时间精度、线程安全、跨平台兼容等)。

以libuv为例的简化示例:

#include <stdio.h>
#include <stdlib.h>
#include <uv.h>

void timer_callback(uv_timer_t* handle) {
    int* arg = handle->data;
    printf("Task %d triggered!\n", *arg);
    free(arg);
    uv_timer_stop(handle);
    uv_close((uv_handle_t*)handle, NULL);
    free(handle);
}

void add_uv_timer(long long ms, void (*cb)(uv_timer_t*), void* arg) {
    uv_timer_t* timer = malloc(sizeof(uv_timer_t));
    uv_timer_init(uv_default_loop(), timer);
    timer->data = arg;
    uv_timer_start(timer, cb, ms, 0); // 单次触发,循环触发则把最后一个参数设为ms
}

int main() {
    int* arg1 = malloc(sizeof(int)); *arg1 = 1;
    add_uv_timer(2000, timer_callback, arg1);
    int* arg2 = malloc(sizeof(int)); *arg2 = 2;
    add_uv_timer(1000, timer_callback, arg2);

    uv_run(uv_default_loop(), UV_RUN_DEFAULT);
    return 0;
}

方案选择建议:

  • 若需要极致兼容性(支持老版本Android):选方案1
  • 若不想自己造轮子,且目标系统支持POSIX定时器:选方案2
  • 若项目已用事件驱动库:直接用方案3

内容的提问来源于stack exchange,提问作者CCJ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:41:34