我有一个工作线程可以从管道中获取工作。像这样的东西
void *worker(void *param) {
while (!work_done) {
read(g_workfds[0], work, sizeof(work));
do_work(work);
}
}
我需要在同一个线程中实现一个1秒的计时器来做一些关于工作的簿记。以下是我的想法:
void *worker(void *param) {
prev_uptime = get_uptime();
while (!work_done) {
// set g_workfds[0] as non-block
now_uptime = get_uptime();
if (now_uptime - prev_uptime > 1) {
do_book_keeping();
prev_uptime = now_uptime;
}
n = poll(g_workfds[0], 1000); // Wait for 1 second else timeout
if (n == 0) // timed out
continue;
read(g_workfds[0], work, sizeof(work));
do_work(work); // This can take more than 1 second also
}
}
我正在使用系统正常运行时间而不是系统时间,因为在此线程运行时系统时间可能会发生变化。我想知道是否还有其他更好的方法来做到这一点。我不想考虑使用另一个线程。使用alarm()
不是一个选项,因为它已被同一进程中的另一个线程使用。这是在Linux环境中实现的。
答案 0 :(得分:3)
我同意webbi在答案中写的大部分内容。但他提出的使用时间而不是正常运行时间的问题存在一个问题。如果系统时间“向前”更新,它将按预期工作。但是如果系统时间缩短了30秒,那么30秒内就不会完成簿记,因为(now_time - prev_time)将是负数(除非使用无符号类型,否则它将继续工作)。
另一种方法是使用带有CLOCK_MONOTONIC的clock_gettime()作为clockid(http://linux.die.net/man/2/clock_gettime)。如果你不需要比秒更小的时间单位,那就有点乱了。
此外,添加用于检测向后时钟跳转的代码也不难。
答案 1 :(得分:2)
我找到了一种更好的方法,但使用timerfd_create()系统调用是特定于Linux的。它负责系统时间的变化。以下是可能的伪代码:
void *worker(void *param) {
int timerfd = timerfd_create(CLOCK_MONOTONIC, 0); // Monotonic doesn't get affected by system time change
// set timerfd to non-block
timerfd_settime(timerfd, 1 second timer); // timer starts
while (!work_done) {
// set g_workfds[0] as non-block
n = poll(g_workfds[0] and timerfd, 0); // poll on both pipe and timerfd and Wait indefinetly
if (timerfd is readable)
do_book_keeping();
if (g_workfds[0] is readable) {
read(g_workfds[0], work, sizeof(work));
do_work(work); // This can take more than 1 second also
}
}
}
看起来更清晰,read()
上的timerfd
会在do_work()
需要很长时间的情况下返回额外的时间,这非常有用,因为do_book_keeping()
期望每秒调用一次。
答案 2 :(得分:0)
我在你的代码中发现了一些奇怪的东西......
poll()有3个args,你传递的是2,第二个arg是你在第一个param的struct数组中传递的结构数,第三个参数是超时。
参考:http://linux.die.net/man/2/poll
除此之外,对我来说这对我来说没问题,当然不是最好的,但是如果不涉及另一个线程或警报(等)就可以了。 您使用时间而不是正常运行时间,如果系统日期发生变化,它可能会导致一个错误,但随后它会继续工作,因为它会更新并继续等待1秒,无论什么时候都是。