C-在select()调用中更新超时值



我想使用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
  }
} 

最新更新