更新select()调用中的超时值

时间:2017-11-30 11:22:36

标签: c linux select timer

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

2 个答案:

答案 0 :(得分:3)

您只需重置超时值即可。 在select返回之前,更新超时值。然后它只包含剩余时间。

如果您根本没有更新,则会出现以下问题:

一旦你的时间用完,你会在以后的每次通话中立即达到超时。

如果使用+=8进行更新,则会增加剩余超时。如果剩下2.5秒,则会达到10.5秒,依此类推。

为避免这种情况,只需将其再次设置为3秒:

while(1) {
  int rc = select(2, NULL, NULL, NULL, &timeout);
  timeout.tv_sec = 3;
  ...

BTW:如果你向select提供了一些你想要等待的文件描述符,你可能会得到更好的结果。

另一个观察结果:

您未在\n来电中添加printf。这不会触发您的stdout。您可能会打印所有内容,但它不会从缓冲区移动到终端。

答案 1 :(得分:1)

你有两个问题的组合。

  • 在Linux上,select修改超时结构以指示剩余时间
  • printf使用缓冲IO。使用printf编写的文本仍保留在程序的缓冲区中,直到打印\nfflush上调用stdout或缓冲区已满。

如果省略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_usec时将timeout.tv_sec设置为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
  }
}