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

C 在UNIX中执行计时器的最佳方法,c,linux,unix,timer,C,Linux,Unix,Timer,我总是注意到人们对UNIX中创建计时器的不同方式有不同的反应 我知道有几种不同的方法可以使事件在UNIX中每隔X秒执行一次: 您可以使用sleeps执行轮询线程pthread\t 您可以在超时的情况下执行select() 我想你可以用其他几种方式使用操作系统 有人能提供一些“最好”或最有效的计时器示例代码,并说明为什么它是最好的?我想使用最有效的机制,但我不确定它是哪一个 出于我们的目的,只要假装每10秒打印一次“Hello World!” 注意:我在这个系统上没有TR1/Boost/etc

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

我知道有几种不同的方法可以使事件在UNIX中每隔X秒执行一次:

  • 您可以使用sleeps执行轮询线程pthread\t
  • 您可以在超时的情况下执行select()
  • 我想你可以用其他几种方式使用操作系统
有人能提供一些“最好”或最有效的计时器示例代码,并说明为什么它是最好的?我想使用最有效的机制,但我不确定它是哪一个

出于我们的目的,只要假装每10秒打印一次“Hello World!”


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

如果您的程序已经线程化,那么使用轮询线程是简单而有效的

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

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


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

使用事件驱动循环,如


这取决于你想做什么。对于在“Hello”之间等待10秒的小例子来说,一个简单的睡眠就足够了,因为您可能会暂停当前线程,直到您的时间结束

如果你的线程在你等待的时候确实在做一些事情,事情就会变得更复杂。如果您正在响应传入的连接,您将已经在使用
select
,在这种情况下,
select
语句的超时对您的内务管理最有意义

如果你在一个紧密的循环中处理东西,你可能会定期轮询一个开始时间,看看你的10秒是否到了

报警
使用适当的信号处理程序也可以正常工作,但在信号处理程序中可以执行的操作存在严重限制。大多数情况下,它需要设置一个需要每隔一段时间进行轮询的标志


简而言之,这取决于线程如何处理事件。

计时器的选择在易用性、可移植性、准确性、丑陋性(全局状态/信号)以及对实时应用程序的适用性等方面有所不同。就我个人而言,最好的情况是,我建议您使用线程滚动您自己的计时器,并使用
clock\u nanosleep
timer\u ABSTIME
标志以及
clock\u MONOTONIC
时钟源。通过这种方式,您可以避免设置系统时间时累积的错误和不连续性,并且您可以自己计算超限。理论上,POSIX
timer\u create
with
SIGEV\u THREAD
delivery应该为您提供相同的属性,但是glibc实现非常糟糕,无法保证在重载情况下不会丢失计时器过期事件。

sleep()是最简单的方法。某些变体上存在usleep()。如果您想要亚秒精度,select(NULL、NULL、NULL和timeout)是最可移植的,除非您编写自己的操作系统特定例程。select()方法的优点是,如果调用被信号中断,则会更新timeout以反映剩余的时间量。如果需要,您可以继续等待

实际上,所有的方法都是等价的;操作系统停止为超时时间安排线程


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

如果你只需要每10秒输出一次“Hello world”,我想睡眠和纳米睡眠就可以了。如果您的任务更加关键,那么您可以看看实时Linux。那里
(www.ee.nmt.edu/~rison/ee352\u fall09/Getting\u Started\u with\u RTLinux.pdf)他们对此提供了很好的指导。使用RTLinux时,您可以使用
pthread\u make\u periodic\u np
使任务周期化,尽管您将负责确保满足实时条件。

这是一个延迟响应,但我想添加我的计时器实现。这是一个线程计时器,需要做一些工作,将互斥体和pthread包装到它们自己的类中(在这方面缺乏RAII)。它也不是跨平台的,因为它使用
gettimeofday()
方法

该代码使用一个调用回调对象的计时器。整个震源可在以下位置看到:


单线程编程比并发编程容易,因此如果您能保证常规调用不会超过间隔,我会选择一个简单的超时(如使用
select()
)。但是,如果调用本身需要更长时间,并且在调用返回之前必须再次启动计时器,则需要多线程。系统已经大量使用多线程,因此这不会是一个问题。把它抛起来作为回答:)嗯,我对实际问题没有什么要说的。我以前使用过boost.asio来满足我的计时需求(ioservice循环在一个单独的线程中运行),但我几乎无法说明各种方法的相对优势……添加了另一条注释@jo
#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;
}
stackoverflow samm$ ./a.out
Hello World!
Hello World!
Hello World!
^C
stackoverflow samm$