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

如何实现不阻塞程序执行的定时器并触发回调函数?

实现非阻塞定时器触发回调的C语言方案

你需要的是非阻塞式的定时器:调用后不阻塞主线程,指定时长到期时自动触发回调函数。以下是两种实用的实现方案:

方案一:使用POSIX定时器API(Linux/Unix专属)

POSIX提供的timer_create系列接口是系统级的非阻塞定时器,支持通过线程或信号触发回调,不会阻塞主线程。下面是贴合你需求的封装实现:

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

// 定义定时器类型
typedef timer_t some_timer_t;

// 回调函数
void callback_fn(union sigval arg) {
    puts("Time is over!");
}

// 创建并启动定时器(单位:毫秒)
some_timer_t create_timer(unsigned int ms) {
    some_timer_t timer;
    struct sigevent sev;
    struct itimerspec its;

    // 配置定时器:到期后创建新线程执行回调(避免信号回调的安全限制)
    sev.sigev_notify = SIGEV_THREAD;
    sev.sigev_notify_function = callback_fn;
    sev.sigev_value.sival_ptr = &timer;
    sev.sigev_notify_attributes = NULL;

    // 创建定时器
    if (timer_create(CLOCK_MONOTONIC, &sev, &timer) == -1) {
        perror("timer_create failed");
        exit(EXIT_FAILURE);
    }

    // 设置定时参数:首次触发在ms毫秒后,仅触发一次
    its.it_value.tv_sec = ms / 1000;
    its.it_value.tv_nsec = (ms % 1000) * 1000000;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;

    // 启动定时器
    if (timer_settime(timer, 0, &its, NULL) == -1) {
        perror("timer_settime failed");
        exit(EXIT_FAILURE);
    }

    return timer;
}

// 关联回调(本方案中已在create_timer完成关联,此函数可留空或做定时器管理)
void when_timer_is_over(some_timer_t timer) {
    // 无需额外操作,回调已通过sigevent绑定
}

int main(void) {
    some_timer_t timer = create_timer(500); // 500毫秒定时器

    // 主线程可正常执行其他任务,不会阻塞
    puts("Main thread is running...");
    // 保持主线程运行,否则程序会直接退出,无法看到回调触发
    while (1) {
        sleep(1);
        puts("Main thread is still working...");
    }

    return 0;
}

说明

  • 使用CLOCK_MONOTONIC时钟,避免系统时间修改影响定时器精度
  • 采用SIGEV_THREAD模式,让系统自动创建线程执行回调,可安全调用标准库函数(如puts)
  • 编译时需链接实时库:gcc timer.c -o timer -lrt

方案二:后台线程实现(跨平台)

如果需要跨平台支持(如Windows),可以用后台线程模拟定时器:线程内等待指定时长后触发回调,主线程正常运行。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>

// 定时器数据结构
typedef struct {
    unsigned int ms;
    void (*callback)(void);
} timer_data_t;

// 定义定时器类型
typedef timer_data_t* some_timer_t;

// 回调函数
void callback_fn() {
    puts("Time is over!");
}

// 定时器线程函数:等待时长后触发回调
void* timer_thread(void* arg) {
    timer_data_t* data = (timer_data_t*)arg;
    // 将毫秒转换为微秒,用usleep等待
    unsigned int usec = data->ms * 1000;
    usleep(usec);
    // 触发回调
    data->callback();
    // 释放内存
    free(data);
    return NULL;
}

// 创建定时器(仅保存时长,暂不启动)
some_timer_t create_timer(unsigned int ms) {
    some_timer_t timer = malloc(sizeof(timer_data_t));
    if (!timer) {
        perror("malloc failed");
        exit(EXIT_FAILURE);
    }
    timer->ms = ms;
    timer->callback = NULL;
    return timer;
}

// 关联回调并启动定时器
void when_timer_is_over(some_timer_t timer, void (*callback)(void)) {
    timer->callback = callback;
    pthread_t tid;
    // 创建后台线程
    if (pthread_create(&tid, NULL, timer_thread, timer) != 0) {
        perror("pthread_create failed");
        free(timer);
        exit(EXIT_FAILURE);
    }
    // 分离线程,避免主线程需要调用pthread_join
    pthread_detach(tid);
}

int main(void) {
    some_timer_t timer = create_timer(500); // 500毫秒定时器
    when_timer_is_over(timer, callback_fn);

    // 主线程执行其他任务,不阻塞
    puts("Main thread is running...");
    while (1) {
        sleep(1);
        puts("Main thread is still working...");
    }

    return 0;
}

说明

  • 跨平台性好,只要支持POSIX线程(Windows可替换为CreateThread)
  • 编译时需链接线程库:gcc timer.c -o timer -lpthread
  • 主线程必须保持运行,否则程序终止后回调无法触发

通用注意事项

  • 你的原示例中main直接return 0,程序会立即退出,回调根本没机会执行,所以必须让主线程持续运行(比如加循环)
  • 如果需要重复触发定时器,可修改方案中的定时参数(方案一修改it_interval,方案二让线程循环等待)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 03:20:14