sleep / usleep (最简单,但不精确)
这是最简单、最直接的定时方法,适用于对精度要求不高的场景。

(图片来源网络,侵删)
sleep(unsigned int seconds): 暂停执行指定的秒数。int usleep(useconds_t usec): 暂停执行指定的微秒数 (1秒 = 1,000,000微秒)。
特点:
- 优点: 非常简单,易于使用。
- 缺点:
- 不精确:
sleep会阻塞整个进程,直到时间结束,如果进程在sleep期间被信号中断,它会提前返回。usleep也是阻塞式的,并且在现代 Linux 系统中已被标记为过时(deprecated)。 - 功能单一: 只能实现一次性延迟,不能周期性执行。
- 不精确:
示例代码:
#include <stdio.h>
#include <unistd.h> // for sleep()
int main() {
printf("程序开始,准备休眠5秒...\n");
sleep(5); // 阻塞5秒
printf("5秒已过,程序继续运行,\n");
printf("准备休眠100毫秒 (100,000微秒)...\n");
// 注意: usleep在较新的系统上可能不可用,且不推荐使用
usleep(100000);
printf("100毫秒已过,\n");
return 0;
}
alarm + signal (信号驱动,一次性)
这种方法使用信号机制,它允许你设置一个定时器,当时间到达时,内核会向你的进程发送一个 SIGALRM 信号,你需要定义一个信号处理函数来响应这个信号。
特点:

(图片来源网络,侵删)
- 优点: 相比
sleep,这是一种非阻塞式的(在信号处理函数执行前),可以让你在等待定时器期间执行其他任务。 - 缺点:
- 一次性:
alarm只能设置一次,每次只能有一个SIGALRM在等待,你需要重新调用alarm来实现周期性。 - 全局状态: 信号处理函数的设计有诸多限制(在信号处理函数中应尽量只调用异步信号安全的函数,如
write,_exit,避免使用printf,malloc等),容易引入 bug。
- 一次性:
示例代码:
#include <stdio.h>
#include <signal.h> // for signal(), SIGALRM
#include <unistd.h> // for alarm()
// 信号处理函数
void alarm_handler(int signum) {
printf("收到 SIGALRM 信号!定时器时间到,\n");
}
int main() {
// 注册 SIGALRM 信号的处理器
signal(SIGALRM, alarm_handler);
printf("程序开始,设置一个3秒的定时器,\n");
alarm(3); // 3秒后发送 SIGALRM 信号
// 主线程可以在这里做其他事情,而不是被阻塞
// 我们用一个简单的循环来模拟
while(1) {
printf("主线程正在运行...\n");
sleep(1);
}
// alarm(0); // 可以取消之前的定时器
return 0;
}
setitimer (功能强大,推荐)
这是最常用、最灵活、最可靠的定时器方法,是 alarm 的超集,它可以设置三种不同的定时器:
ITIMER_REAL: 以真实时间计时,时间到发送SIGALRM信号。ITIMER_VIRTUAL: 以进程在用户态下运行的时间计时,时间到发送SIGVTALRM信号。ITIMER_PROF: 以进程在用户态和内核态下运行的总时间计时,时间到发送SIGPROF信号。
setitimer 可以实现一次性和周期性定时,并且可以获取剩余时间。
特点:

(图片来源网络,侵删)
- 优点:
- 高精度: 比前两种方法更精确。
- 功能强大: 支持一次性、周期性定时。
- 灵活性: 提供三种不同的时间基准。
- 非阻塞: 和
alarm一样,通过信号机制工作,不会阻塞主线程。
- 缺点:
- 同样需要处理信号,有信号处理函数的限制。
- API 比
sleep复杂一些。
数据结构:
struct itimerval 用于定义定时器的参数:
struct itimerval {
struct timeval it_interval; // 定时器周期性重复的时间间隔
struct timeval it_value; // 第一次触发的时间倒计时
};
struct timeval {
long tv_sec; // 秒
long tv_usec; // 微秒
};
示例代码 (周期性定时器): 这个例子演示了如何每2秒打印一次 "定时器触发!",同时主线程不受影响,持续打印自己的信息。
#include <stdio.h>
#include <signal.h> // for signal(), SIGALRM
#include <unistd.h> // for getpid()
#include <sys/time.h> // for setitimer(), struct itimerval
// 全局变量,用于计数
int timer_count = 0;
// 信号处理函数
void timer_handler(int signum) {
timer_count++;
printf("定时器触发!这是第 %d 次,\n", timer_count);
}
int main() {
// 注册 SIGALRM 信号的处理器
signal(SIGALRM, timer_handler);
// 初始化 itimerval 结构体
struct itimerval timer;
// 设置第一次触发的时间为2秒
timer.it_value.tv_sec = 2;
timer.it_value.tv_usec = 0;
// 设置之后每次重复的时间间隔为2秒 (周期性)
timer.it_interval.tv_sec = 2;
timer.it_interval.tv_usec = 0;
// 启动定时器
// ITIMER_REAL: 使用真实时间,触发 SIGALRM
if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
perror("setitimer");
return 1;
}
printf("定时器已启动,进程ID: %d\n", getpid());
printf("主线程开始运行...\n");
// 主线程可以执行其他任务,不会被定时器阻塞
while(1) {
sleep(1); // 模拟主线程工作
printf("主线程正在运行...\n");
}
return 0;
}
总结与对比
| 方法 | 阻塞方式 | 精度 | 周期性 | 复杂度 | 适用场景 |
|---|---|---|---|---|---|
sleep/usleep |
阻塞 | 低 | 否 | 非常低 | 简单的延时任务,对精度要求不高。 |
alarm+signal |
非阻塞 (信号驱动) | 中 | 否 (需手动重置) | 低 | 一次性任务,需要响应事件,且精度要求不高。 |
setitimer |
非阻塞 (信号驱动) | 高 | 是 | 中 | 强烈推荐,需要高精度、周期性、一次性定时的各种复杂场景。 |
如何选择?
- 如果你只是想让程序“等一下”,并且不关心等待期间是否还能做别的事,用
sleep最简单。 - 如果你需要响应一个一次性的事件,并且希望程序在等待时能做点别的事,可以用
alarm。 - 如果你需要高精度的定时,或者需要周期性地执行某个任务,毫无疑问,应该使用
setitimer,它是 Linux 下功能最全面的定时器解决方案。
对于大多数需要定时器的应用场景,setitimer 是最佳选择。
