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

为何使用SIGEV_THREAD时timer_create会提前创建线程?

为什么使用SIGEV_THREAD时timer_create阶段就创建了线程?

这是个很细节的问题,其实背后是POSIX标准允许的实现差异加上glibc(Linux常用C标准库)的具体优化策略导致的,我来一步步给你拆解清楚:

1. POSIX标准的灵活性

POSIX对于SIGEV_THREAD模式的定义是:定时器到期时需执行指定的sigev_notify_function,但并没有强制要求必须在到期时才创建线程。标准允许两种实现方式:

  • 方式一:每次定时器到期时动态创建新线程来执行回调
  • 方式二:提前创建一个常驻线程,专门负责处理所有SIGEV_THREAD类型的定时器事件,到期时直接在这个线程里(或通过线程池)执行回调

Linux上的glibc选择了第二种方式,这就是你看到timer_create时就出现新线程的核心原因。

2. glibc的具体实现逻辑

当你调用timer_create并设置sigev_notify = SIGEV_THREAD时,glibc会立即创建一个名为__timer_thread的内部线程(也就是你线程列表里SPID为9946的那个)。这个线程的作用是:

  • 监听所有使用SIGEV_THREAD的定时器到期事件
  • 当定时器到期时,调用你传入的sigev_notify_function并传递sigev_value参数
  • 对于你代码里的一次性定时器(it_interval.tv_sec=0),这个线程在处理完一次回调后可能会退出,但创建动作确实是在timer_create阶段完成的

这种设计是为了性能优化——频繁创建销毁线程的开销很大,提前复用一个线程(或线程池)能显著提升定时器密集场景下的运行效率。

3. 和SIGEV_SIGNAL的对比

当使用SIGEV_SIGNAL时,定时器到期的通知是通过信号发送给进程的:

  • 进程可以通过注册信号处理函数,或者用sigwait等方式等待信号
  • 整个过程不需要额外线程,信号处理是在进程的现有线程(或内核调度的线程)中执行的,所以timer_create阶段不会创建新线程

你的代码与线程列表验证

整理后的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <assert.h>
#include <string.h>

void timer_thread(union sigval sv) {
    printf("Timer callback triggered, value: %d\n", sv.sival_int);
}

int main(){
    printf ("My process ID %d\n", getpid());
    int status = 0;
    timer_t timer_id;
    memset (&timer_id, 0, sizeof (timer_t));
    int j = 10;
    struct itimerspec ts;
    struct sigevent se;

    se.sigev_notify = SIGEV_THREAD;
    se.sigev_value.sival_int = j;
    se.sigev_notify_function = timer_thread;

    ts.it_value.tv_sec = 3;
    ts.it_value.tv_nsec = 0;
    ts.it_interval.tv_sec =0;
    ts.it_interval.tv_nsec =0;

    status = timer_create (CLOCK_REALTIME, &se, &timer_id);
    printf ("timer_id is %ld\n",(long int)timer_id);
    assert (!status && "Create timer");

    status = timer_settime (timer_id, 0, &ts, 0);
    assert (!status && "Set timer");

    // 注意:原代码直接return会导致进程提前退出,需等待定时器触发
    sleep(5);
    return 0;
}

你观察到的线程列表:

PID SPID TTY TIME CMD
9945 9945 pts/20 00:00:18 timer_create
9945 9946 pts/20 00:00:00 timer_create

其中SPID=9946的就是glibc提前创建的定时器处理线程。

额外验证点

如果你把代码改成周期性定时器(比如ts.it_interval.tv_sec=2),运行进程后再查看线程列表,会发现始终只有这两个线程,不会每次到期都新增——这就验证了glibc是复用线程而非动态创建的逻辑。

内容的提问来源于stack exchange,提问作者Mathi.J

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:56:36