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

Linux定时器SIGEV_THREAD使用及回调修改枚举值问题

解决定时器回调段错误与多实例状态修改问题

咱们先拆解下你遇到的两个核心问题:

  1. 编译警告+段错误:根源是回调函数的签名不匹配——SIGEV_THREAD要求的回调函数类型是void (*)(union sigval)(传值),但你写的Hndlr是接收union sigval *(传指针),类型不兼容导致警告,运行时把sigval值当成指针解引用,直接触发段错误。
  2. 无法修改原结构体的TimerStatus:你试图通过sigval.sival_int传递枚举值,但这是传值,修改的只是副本,根本碰不到t1里的原始值;而且要支持多定时器,得让每个回调能定位到对应的Info_t实例。

修正方案:传递结构体实例指针

正确的做法是把Info_t的实例指针通过sigev_value.sival_ptr传给回调,这样回调就能直接操作原结构体的成员,天然支持多定时器(每个定时器绑定自己的Info_t实例)。

修正后的完整代码

#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>

typedef struct Info {
    enum Status { Start = 1, Expire = 2 } TimerStatus;
    int data;
    timer_t timerId;
} Info_t;

// 修正回调函数签名:接收union sigval值,而非指针
void Hndlr(union sigval sv) {
    // 从sigval中取出Info_t实例的指针
    Info_t *info = (Info_t*)sv.sival_ptr;
    // 直接修改原结构体的TimerStatus
    info->TimerStatus = Expire;
    printf("Timer expired! Instance TimerStatus set to Expire\n");
}

int TimerInit(Info_t *Type) {
    struct sigevent sev;
    sev.sigev_notify = SIGEV_THREAD;
    // 传递当前Info_t实例的指针,而非单个成员
    sev.sigev_value.sival_ptr = Type;
    // 绑定正确签名的回调函数
    sev.sigev_notify_function = Hndlr;
    sev.sigev_notify_attributes = NULL; // 用NULL更规范

    // 检查timer_create的返回值,避免错误
    if (timer_create(CLOCK_REALTIME, &sev, &(Type->timerId)) == -1) {
        perror("timer_create failed");
        return -1;
    }
    return 0;
}

int main(int argc, char const *argv[]) {
    Info_t t1, t2; // 测试多定时器实例
    t1.TimerStatus = Start;
    t1.data = 100;
    t2.TimerStatus = Start;
    t2.data = 200;

    if (TimerInit(&t1) == -1 || TimerInit(&t2) == -1) {
        return 1;
    }

    // 启动定时器:设置1秒后到期,只触发一次
    struct itimerspec its;
    its.it_value.tv_sec = 1;
    its.it_value.tv_nsec = 0;
    its.it_interval.tv_sec = 0;
    its.it_interval.tv_nsec = 0;

    if (timer_settime(t1.timerId, 0, &its, NULL) == -1) {
        perror("timer_settime t1 failed");
        return 1;
    }
    if (timer_settime(t2.timerId, 0, &its, NULL) == -1) {
        perror("timer_settime t2 failed");
        return 1;
    }

    // 轮询检查状态
    while (1) {
        if (t1.TimerStatus == Expire) {
            printf("t1 expired! Data: %d\n", t1.data);
            t1.TimerStatus = Start; // 重置状态,可再次触发
            // 重新启动t1定时器
            timer_settime(t1.timerId, 0, &its, NULL);
        }
        if (t2.TimerStatus == Expire) {
            printf("t2 expired! Data: %d\n", t2.data);
            t2.TimerStatus = Start;
            timer_settime(t2.timerId, 0, &its, NULL);
        }
        sleep(1);
    }
    return 0;
}

关键修改点说明

  • 回调签名修正:把Hndlr的参数从union sigval *改成union sigval,匹配SIGEV_THREAD要求的函数类型,彻底消除编译警告和段错误。
  • 传递实例指针:sev.sigev_value.sival_ptr = Type把整个Info_t实例的地址传给回调,这样回调能直接修改原结构体的TimerStatus,而不是副本。
  • 多实例支持:每个定时器绑定自己的Info_t实例,回调通过指针区分不同实例,各自修改自己的状态,互不干扰。
  • 错误检查:补充了timer_create和timer_settime的返回值检查,方便排查问题。

额外注意事项

  • 如果你的场景中多个定时器回调可能同时修改共享资源(而非各自的实例),需要加互斥锁(pthread_mutex_t)保证线程安全。
  • CLOCK_REALTIME是系统实时时钟,如果需要不受系统时间调整影响的定时器,可以改用CLOCK_MONOTONIC。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 15:42:58