在UNIX中执行计时器的最佳方法

时间:2011-08-25 14:37:49

标签: c linux unix timer

我总是注意到人们对在UNIX中创建计时器的不同方式有不同的反应。

我知道在UNIX中每隔X秒执行一次事件的几种不同方式:

  • 你可以做一个轮询线程 - pthread_t with sleeps;
  • 你可以用超时做一个select();
  • 我猜您可以通过其他几种方式使用操作系统。

有人可以提供一些示例代码,使用“最佳”或最有效的方式来执行计时器并说明为什么它是最好的?我想使用最有效的机制,但我不确定它是哪一个!

出于我们的目的,只是假装你正在打印“Hello World!”每10秒一次。

注意:我没有TR1 / Boost / etc。在这个系统上,所以请保持它直接进行C / C ++和UNIX系统调用。很抱歉没有第一次提及:)

7 个答案:

答案 0 :(得分:6)

这取决于你想要做什么。一个简单的睡眠就足以让你在“Hello”之间等待10秒的简单例子,因为你可以暂停你当前的线程,直到你的时间结束。

如果您的线程在您等待期间实际执行某些操作,事情会变得更复杂。如果您正在响应传入的连接,那么在这种情况下您已经使用selectselect语句的超时对您的内务管理最有意义。

如果您正在紧密循环处理内容,您可能会定期轮询开始时间以查看您的10秒是否已启动。

具有适当信号处理程序的

alarm也可以正常工作,但在信号处理程序中可以执行的操作存在严重限制。大多数情况下,它涉及设置一个需要经常轮询的标志。

简而言之,它归结为您的线程如何处理事件。

答案 1 :(得分:3)

使用像Boost.Asio

这样的事件驱动循环
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class Timer
{
public:
    Timer(
            boost::asio::io_service& io_service
         ) :
        _impl( io_service )
    {
        this->wait();
    }

private:
    void wait()
    {
        _impl.expires_from_now( boost::posix_time::seconds(10) );
        _impl.async_wait(
                boost::bind(
                    &Timer::handler,
                    this,
                    _1
                    )
                );
    }

    void handler(
            const boost::system::error_code& error
            )
    {
        if ( error ) {
            std::cerr << "could not wait: " << boost::system::system_error(error).what() << std::endl;
            return;
        }

        std::cout << "Hello World!" << std::endl;

        this->wait();
    }

private:
    boost::asio::deadline_timer _impl;
};

int main()
{
    boost::asio::io_service io;

    Timer t( io );

    io.run();

    return 0;
}

build&amp;运行:

stackoverflow samm$ ./a.out
Hello World!
Hello World!
Hello World!
^C
stackoverflow samm$ 

答案 2 :(得分:2)

如果您的程序已经过线程化,那么使用poll线程非常简单有效。

如果您的程序尚未进行线程化,则会变得更加棘手。你能用一个单独的程序来完成这项工作吗?如果是这样,那么使用多处理而不是多线程。

如果您不能使用自主控制线程(进程或线程),那么它取决于您的程序的组织方式,这就是为什么有许多首选替代方案(它们在不同情况下是首选)。它还取决于您需要的计时准确性。由于存在多秒间隙且没有硬实时响应要求,您可以使用警报并且SIGALRM处理程序可以设置标志,并且您的主处理循环可以在方便,明智的点监视标志并执行活动。这有些混乱(因为信号很杂乱),但如果没有更详细的描述你的要求和约束,很难知道还有什么建议。

因此,总而言之,没有一个通用的最佳解决方案,因为不同的程序是以不同的风格和不同的约束条件编写的。

答案 3 :(得分:1)

如何定时器的选择在易用性,便携性,准确性,丑陋性(全局状态/信号)以及实时应用的适用性方面各不相同。就个人而言,对于所有世界中最好的,我建议用一个线程滚动你​​自己的计时器,并使用带有clock_nanosleep标志和TIMER_ABSTIME时钟源的CLOCK_MONOTONIC。通过这种方式,您可以避免累积错误和设置系统时间的不连续性,并且您可以自己计算超支。从理论上讲,带有timer_create传递的POSIX SIGEV_THREAD应该为您提供相同的属性,但是glibc实现非常糟糕,无法保证在重负载下它不会丢失计时器到期事件。

答案 4 :(得分:1)

sleep()是最简单的方法。 usleep()存在于某些变体上。如果你想要亚秒精度,选择(NULL,NULL,NULL和amp; timeout)是最便携的,除非你编写自己的特定于OS的例程。 select()方法的优点是更新超时以反映呼叫被信号中断时剩余的时间量。如果需要,您可以恢复等待。

真的,所有的方法都是等价的;操作系统停止在超时期限内调度线程。

Blagovestus建议的SIGALARM方法(有些)是不可移植的,即使它应该在Linux上运行。

答案 5 :(得分:1)

这是一个迟到的回复,但我想添加我的计时器实现。它是一个线程计时器,需要一些工作,在自己的类中包含互斥锁和pthreads(在这方面缺乏RAII)。它也不是跨平台的,因为它使用gettimeofday()方法。

代码使用调用CallBack对象的计时器。

可以看到整个来源

http://matthewh.me/playground/browser/branches/C%2B%2B/c%2B%2B_callbacks

答案 6 :(得分:0)

如果你只需要每10秒输出一次“Hello world”,我想睡眠和纳米睡眠就会发生。如果您的任务在某种程度上更为关键,那么您可以查看RealTime Linux。那里 (www.ee.nmt.edu/~rison/ee352_fall09/Getting_Started_with_RTLinux.pdf)他们就此提出了一个很好的指导。使用RTLinux时,您可以使用pthread_make_periodic_np定期执行任务,但您将负责确保满足实时条件。