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(¤t_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
相关产品推荐
相关产品推荐

