我想使用select()实现一个计时器,我希望它在每3秒的间隔之后到达超时代码。
如果我删除" timeout.tv_sec = 8;"从我的代码中等待3秒钟,然后开始连续打印"超时",然后使用" timeout.tv_sec = 8"打印什么都没有打印,该程序会被卡住。什么都没有打印。有人可以向我解释一下我在做什么错吗?我的代码如下:基本上,我希望它以3秒的间隔打印时间。
struct timeval timeout;
timeout.tv_sec = 3;
while(1) {
int rc = select(2, NULL, NULL, NULL, &timeout);
if(rc == 0)
{
printf(" time out ");
timeout.tv_sec+=8;
}
if (rc < 0)
{
printf(" Error ");
continue;
}
else
{
printf(" process ");// some instructions
}
}
您只需要重置超时值即可。在select
返回之前,更新了超时值。然后它仅包含剩余时间。
如果您根本不更新,则会遇到以下问题:
时间用完后,您会立即在以下电话中进行超时。
如果使用+=8
更新,则会增加剩余的超时。如果剩下2.5秒,它将获得10.5秒,依此类推。
要避免这种情况,只需将其设置为3s:
while(1) {
int rc = select(2, NULL, NULL, NULL, &timeout);
timeout.tv_sec = 3;
...
btw:如果您为您要等待的select
提供了一些文件描述,则可能会获得更好的结果。
另一个观察:
您不会在printf
调用中添加n
。这不会触发您的stdout
冲洗。您可能会打印所有内容,但它只是没有从缓冲区移到终端。
您有两个问题的组合。
- 在Linux上,
select
修改了超时结构,以指示剩下多少时间 -
printf
使用缓冲io。使用printf
编写的文本保留在程序中的缓冲区中,直到打印n
或在stdout
上调用fflush
或缓冲区已满。
如果省略了timeout.tv_sec+=8
,则您的代码等待3秒钟,然后打印time out
,除非它仅用于程序中的内部缓冲区。下次循环时,超时为零,因此该程序立即将time out
打印到缓冲区。循环的任何后续执行都相同。很快,缓冲区被填充,并且该程序将其冲洗到stdout
,此时您会在屏幕上看到它。
如果您在第一次迭代中留在timeout.tv_sec+=8
中,则select
等待3秒钟,在第二个迭代中,每次迭代,则等待8秒钟。这意味着填充缓冲区需要更长的时间,因此在您看到任何输出之前,它将需要更长的时间。您打印10 char
s。如果内部缓冲区为4096字节,则完全需要55分钟才能打印任何东西。
要修复它,要么在每个打印语句上放置一个n
,要么定期使用fflush
。
您需要做另一件事,那就是每次设置timeout.tv_sec
时将timeout.tv_usec
设置为0。您的代码,如它的那样,将struct
一半放在一半的情况下,因此调用了未定义的行为。
struct timeval timeout;
while(1) {
timeout.tv_sec = 3 ;
timeout.tv_usec = 0; // Otherwise UB!
int rc = select(2, NULL, NULL, NULL, &timeout);
if(rc == 0)
{
printf(" time out ");
}
if (rc < 0)
{
printf(" Error ");
break; // The error is unlikely to go away next time around
}
else
{
printf(" process ");// some instructions
}
}