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

如何让C语言while(1)循环以固定10000Hz频率运行并满足双退出条件?

实现固定10000Hz频率的C语言循环(多进程时间跟踪版)

要解决循环频率不准、退出条件不精准的问题,我们可以用多进程分离计时与计算任务的方案:主进程负责维持固定频率的循环执行计算,子进程专门跟踪运行时长,到点触发退出;同时主进程自身监控迭代次数,满足任一退出条件即终止循环。

核心思路

  • 主进程:执行循环内的计算逻辑,通过高精度定时器控制每次循环的总周期(100us,对应10000Hz),同时统计迭代次数,达到指定次数时主动退出。
  • 子进程:独立跟踪运行时长,当达到设定的最大运行时间时,向主进程发送终止信号,触发主进程退出循环。

完整代码实现

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

#define TARGET_FREQ 10000  // 目标频率:10000Hz
#define PERIOD_NS (1000000000 / TARGET_FREQ)  // 单次循环周期:100微秒(100000纳秒)

volatile sig_atomic_t exit_flag = 0;

// 信号处理函数:收到子进程的终止信号时设置退出标志
void sig_handler(int signum) {
    exit_flag = 1;
}

int main(int argc, char *argv[]) {
    if (argc != 3) {
        fprintf(stderr, "用法: %s <最大运行时长(秒)> <最大迭代次数>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    const int max_seconds = atoi(argv[1]);
    const int max_iterations = atoi(argv[2]);
    int loop_count = 0;

    // 注册SIGINT信号处理,响应子进程的终止通知
    if (signal(SIGINT, sig_handler) == SIG_ERR) {
        perror("注册信号处理失败");
        exit(EXIT_FAILURE);
    }

    // 创建子进程负责计时
    pid_t pid = fork();
    if (pid < 0) {
        perror("创建子进程失败");
        exit(EXIT_FAILURE);
    } else if (pid == 0) {
        // 子进程逻辑:等待指定时长后向主进程发送终止信号
        sleep(max_seconds);
        kill(getppid(), SIGINT);
        exit(EXIT_SUCCESS);
    } else {
        // 主进程逻辑:维持固定频率循环
        struct timespec start, end, sleep_time;
        long long elapsed_ns;

        while (!exit_flag && loop_count < max_iterations) {
            // 记录循环开始时间(使用单调时钟,不受系统时间调整影响)
            clock_gettime(CLOCK_MONOTONIC, &start);

            // --------------------------
            // 此处替换为你的计算逻辑
            // Some calculations
            // --------------------------

            // 更新迭代次数
            loop_count++;

            // 计算当前循环已耗时
            clock_gettime(CLOCK_MONOTONIC, &end);
            elapsed_ns = (end.tv_sec - start.tv_sec) * 100000000LL + (end.tv_nsec - start.tv_nsec);

            // 补全剩余时间,保证单次循环总时长为PERIOD_NS
            if (elapsed_ns < PERIOD_NS) {
                sleep_time.tv_sec = 0;
                sleep_time.tv_nsec = PERIOD_NS - elapsed_ns;
                // 处理nanosleep被信号打断的情况,确保休眠足够时长
                while (nanosleep(&sleep_time, &sleep_time) != 0);
            }
            // 若计算耗时超过周期,直接进入下一次循环(无法保证频率,只能尽量追赶)
        }

        // 清理子进程资源
        kill(pid, SIGKILL);
        waitpid(pid, NULL, 0);

        printf("循环终止:实际迭代次数=%d,设定运行时长=%d秒\n", loop_count, max_seconds);
    }

    return EXIT_SUCCESS;
}

关键细节说明

  1. 高精度计时:
    使用CLOCK_MONOTONIC时钟,它是单调递增的,不会受系统时间修改(如NTP同步、手动调时间)影响,保证计时的准确性。

  2. 信号安全的退出控制:
    用volatile sig_atomic_t类型的exit_flag,确保信号处理函数修改的变量能被主进程及时感知,同时避免竞态条件。

  3. 周期补时逻辑:
    每次循环计算已消耗的时间,若小于目标周期(100us),则用nanosleep()休眠剩余时间;若计算耗时超过周期,直接进入下一次循环(这种情况无法保证频率,需优化计算逻辑)。

  4. 高精度子进程计时(可选):
    若需要秒级以下的精确时长控制(如1.5秒),可将子进程的sleep()替换为基于clock_gettime的循环计时:

    struct timespec start, current;
    clock_gettime(CLOCK_MONOTONIC, &start);
    while (1) {
        clock_gettime(CLOCK_MONOTONIC, &current);
        long long elapsed = (current.tv_sec - start.tv_sec) * 100000000LL + (current.tv_nsec - start.tv_nsec);
        if (elapsed >= (long long)max_seconds * 1000000000LL) {
            kill(getppid(), SIGINT);
            break;
        }
        // 短休眠避免空循环占用CPU
        nanosleep(&(struct timespec){0, 1000000}, NULL);
    }
    

编译与运行

  • 编译命令(部分系统需链接实时库):
    gcc -o fixed_freq_loop fixed_freq_loop.c -lrt
    
  • 运行示例(运行1秒或10000次迭代,满足任一条件即退出):
    ./fixed_freq_loop 1 10000
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 23:40:22