我总是注意到人们对在UNIX中创建计时器的不同方式有不同的反应。
我知道在UNIX中每隔X秒执行一次事件的几种不同方式:
有人可以提供一些示例代码,使用“最佳”或最有效的方式来执行计时器并说明为什么它是最好的?我想使用最有效的机制,但我不确定它是哪一个!
出于我们的目的,只是假装你正在打印“Hello World!”每10秒一次。
注意:我没有TR1 / Boost / etc。在这个系统上,所以请保持它直接进行C / C ++和UNIX系统调用。很抱歉没有第一次提及:)
答案 0 :(得分:6)
这取决于你想要做什么。一个简单的睡眠就足以让你在“Hello”之间等待10秒的简单例子,因为你可以暂停你当前的线程,直到你的时间结束。
如果您的线程在您等待期间实际执行某些操作,事情会变得更复杂。如果您正在响应传入的连接,那么在这种情况下您已经使用select
,select
语句的超时对您的内务管理最有意义。
如果您正在紧密循环处理内容,您可能会定期轮询开始时间以查看您的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
定期执行任务,但您将负责确保满足实时条件。