【问题标题】:ways of implementing timer in worker thread in C在 C 中的工作线程中实现计时器的方法
【发布时间】:2011-01-27 13:37:10
【问题描述】:

我有一个从管道获取工作的工作线程。像这样的

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 环境中实现的。

【问题讨论】:

  • 嘿嘿.. 它实际上是一回事。
  • 其实不一样,select()有固定数量的socket会读取(1024)而poll()不会。

标签: c multithreading unix timer


【解决方案1】:

我同意 webbi 在他的回答中写的大部分内容。但是他提出的使用时间而不是正常运行时间的建议存在一个问题。如果系统时间“向前”更新,它将按预期工作。但是,如果系统时间被设置了 30 秒,那么 30 秒内将不会进行簿记,因为 (now_time - prev_time) 将是负数(除非使用无符号类型,在这种情况下它仍然可以工作)。

另一种方法是使用clock_gettime() 和CLOCK_MONOTONIC 作为clockid (http://linux.die.net/man/2/clock_gettime)。如果您不需要小于秒的时间单位,那就有点麻烦了。

此外,添加代码来检测向后时钟跳变也不难。

【讨论】:

  • 如果您不需要小于秒的时间单位,只需使用 tv_sec 字段 :)
【解决方案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
    }
  }
}

它看起来更干净,timerfd 上的read() 返回额外的时间,以防do_work() 需要很长时间,这非常有用,因为do_book_keeping() 预计每秒都会被调用。

【讨论】:

  • 这是一个很好的解决方案,但请注意 timerfd 是相对较新的 Linux 新增功能。
【解决方案3】:

我在你的代码中发现了一些奇怪的东西......

poll() 有 3 个参数,你传递 2,第二个参数是你在第一个参数的结构数组中传递的结构的数量,第三个参数是超时。

参考:http://linux.die.net/man/2/poll

除此之外,这种解决方法对我来说很好,当然它不是最好的,但是不涉及另一个线程或警报()等也很好。 您使用的是时间而不是正常运行时间,如果系统日期发生更改,它可能会导致您出现一个错误,但随后它将继续工作,因为它将被更新并继续等待 1 秒,无论时间是什么。

【讨论】:

  • 我了解 poll() 参数。它主要是一个伪代码。我打算在 poll() 旁边写一条评论,以原谅我使用较少的参数:)。我想你是对的关于使用时间。获得正常运行时间涉及读取 /proc/uptime 和解析它的额外开销。
猜你喜欢
  • 2023-03-20
  • 1970-01-01
  • 2018-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-07
  • 2015-07-24
  • 1970-01-01
相关资源
最近更新 更多