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

C语言忙等待时间不一致问题排查与修复求助

角色循环逻辑异常死亡的问题分析与修复

问题描述

需要实现角色逻辑:循环执行200微秒「进食」、200微秒「休眠」,若角色超过time_to_die微秒未进食则死亡。在main函数中设置time_to_die.tv_usec = 1000(即1000微秒),预期程序永久循环,但运行一段时间后busy_wait函数某次执行耗时约为设定值的5倍,导致角色死亡。补充说明:此前混淆毫秒与微秒是问题根源。

原代码

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

struct timeval  time_add_microseconds(struct timeval time, long long microseconds)
{
    
    time.tv_usec += microseconds;
    while (time.tv_usec >= 1000000)
    {
        time.tv_sec += 1;
        time.tv_usec -= 1000000;
    }
    return (time);
}


short   time_compare(struct timeval time_one, struct timeval time_two)
{
    if (time_one.tv_sec != time_two.tv_sec)
    {
        if (time_one.tv_sec > time_two.tv_sec)
            return (1);
        else
            return (-1);
    }
    else
    {
        if (time_one.tv_usec > time_two.tv_usec)
            return (1);
        else if (time_one.tv_usec == time_two.tv_usec)
            return (0);
        else 
         return (-1);
    }
}

// Wait until interval in microseconds has passed or until death_time is reached.
void    busy_wait(int interval, struct timeval last_eaten_time, struct timeval time_to_die)
{
    struct timeval      time;
    struct timeval      end_time;
    struct timeval      death_time;

    gettimeofday(&time, NULL);
    end_time = time_add_microseconds(time, interval);
    death_time = time_add_microseconds(last_eaten_time, time_to_die.tv_sec * 1000000ULL + time_to_die.tv_usec);
    while (time_compare(time, end_time) == -1)
    {
        gettimeofday(&time, NULL);
        if (time_compare(time, death_time) >= 0)
        {
            printf("%llu died\n", time.tv_sec * 1000000ULL + time.tv_usec);
            exit(1);
        }
    }
}

int main(void)
{
    struct timeval time;
    struct timeval time_to_die = { .tv_sec = 0, .tv_usec = 1000};
    struct timeval last_eaten_time = { .tv_sec = 0, .tv_usec = 0 };

    while (true)
    {
        gettimeofday(&time, NULL);
        printf("%llu eating\n", time.tv_sec * 1000000ULL + time.tv_usec);
        last_eaten_time = time;
        busy_wait(200, last_eaten_time, time_to_die);

        gettimeofday(&time, NULL);
        printf("%llu sleeping\n", time.tv_sec * 1000000ULL + time.tv_usec);
        busy_wait(200, last_eaten_time, time_to_die);
    }
}

原因分析

  • 忙等机制的不可靠性:原代码使用轮询式忙等(busy_wait),进程会持续占用CPU,但操作系统的调度器可能会将该进程挂起(比如切换到高优先级进程),导致实际等待时间远超设定的200微秒。当某次挂起时长超过600微秒时,加上之前的两次200微秒等待,总未进食时间会突破1000微秒的阈值,触发死亡逻辑。
  • 时间单位混淆的潜在影响:虽然你提到混淆毫秒与微秒是根源,但当前代码中time_to_die设置为1000微秒(1毫秒),理论上循环总耗时400微秒不会触发死亡,但忙等被调度打断的情况直接放大了这个问题,让阈值被轻易突破。

修复方案

核心是用usleep替代忙等,让进程主动休眠释放CPU,由系统精准唤醒,同时分段休眠并定期检查死亡条件,避免长时间休眠错过触发时机。

修复后代码

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

struct timeval  time_add_microseconds(struct timeval time, long long microseconds)
{
    time.tv_usec += microseconds;
    while (time.tv_usec >= 1000000)
    {
        time.tv_sec += 1;
        time.tv_usec -= 1000000;
    }
    return time;
}

short   time_compare(struct timeval time_one, struct timeval time_two)
{
    if (time_one.tv_sec != time_two.tv_sec)
    {
        return time_one.tv_sec > time_two.tv_sec ? 1 : -1;
    }
    else
    {
        if (time_one.tv_usec > time_two.tv_usec)
            return 1;
        else if (time_one.tv_usec == time_two.tv_usec)
            return 0;
        else 
            return -1;
    }
}

void    safe_wait(int interval, struct timeval last_eaten_time, struct timeval time_to_die)
{
    struct timeval      current_time;
    struct timeval      death_time;
    long long           remaining_time = interval;

    death_time = time_add_microseconds(last_eaten_time, time_to_die.tv_sec * 1000000ULL + time_to_die.tv_usec);
    
    // 分段休眠,定期检查死亡条件
    while (remaining_time > 0)
    {
        // 每次最多休眠100微秒,避免长时间休眠错过死亡检查
        int wait_chunk = remaining_time > 100 ? 100 : (int)remaining_time;
        usleep(wait_chunk);
        remaining_time -= wait_chunk;

        gettimeofday(&current_time, NULL);
        if (time_compare(current_time, death_time) >= 0)
        {
            printf("%llu died\n", (unsigned long long)current_time.tv_sec * 1000000ULL + current_time.tv_usec);
            exit(1);
        }
    }
}

int main(void)
{
    struct timeval time;
    struct timeval time_to_die = { .tv_sec = 0, .tv_usec = 1000};
    struct timeval last_eaten_time;

    while (true)
    {
        gettimeofday(&time, NULL);
        printf("%llu eating\n", (unsigned long long)time.tv_sec * 1000000ULL + time.tv_usec);
        last_eaten_time = time;
        safe_wait(200, last_eaten_time, time_to_die);

        gettimeofday(&time, NULL);
        printf("%llu sleeping\n", (unsigned long long)time.tv_sec * 1000000ULL + time.tv_usec);
        safe_wait(200, last_eaten_time, time_to_die);
    }
}

修复说明

  • 替换busy_wait为safe_wait,使用usleep进行分段休眠,每次最多休眠100微秒,既保证总等待时间接近设定值,又能定期检查死亡条件。
  • 避免了忙等导致的高CPU占用和调度延迟问题,让等待时间更可控。
  • 优化了变量声明和代码可读性,移除了冗余的括号。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 20:01:02