c - 如何在C中创建非单拍定时器?

标签 c linux timer

我需要在嵌入式linux的c代码中使用一个非单点定时器(比如qt库中的qtimer)(没有单点定时器,我的意思是在调用“停止定时器”之前无限期地触发一次x秒,而不是只触发一次或在计数时阻塞代码的定时器)。
一些可用的库确实使用信号处理程序实现了这样的计时器,但我希望避免使用这样的系统(I learned that is not the best solution)。我知道我可以通过重新启动计时器(再次调用它)来模拟我想要的单点计时器,这是一个可以接受的解决方案(实际上我谈到的库是这样工作的),但我不知道如何在不阻止正在运行的代码的情况下实现它,直到计时器被触发。
还有一件事:我需要有能力实现更多而不仅仅是其中一个(这里是信号处理程序不再是一个可行的解决方案)。
我怎么能解决这个问题呢?接近qt的qtimer所提供的,越好!

最佳答案

如果您确实需要以不同的间隔/时间指定数量的触发器,那么在我的经验中,专用计时器线程(如另一个答案中的nneonneo所述)的陷阱数量最少。
计时器是一个有限的资源(可用计时器的数量是可配置的,并且随系统的不同而不同,因此您不能做任何扫掠语句,例如“我确信有足够的时间来满足我的目的”)。
除非使用SA_RESTART标志,否则信号中断阻塞系统调用;即使如此,也有一些异常(有关详细信息,请参阅man 7 signal信号处理程序中断系统调用和库函数一章)。
专用计时器线程围绕两个组件构建:
包含所有计时器事件的队列、列表、树或堆
一个典型的实现只需要知道下一个事件何时发生,所以amin-heap或apriority queue非常有效。我发现了一个MIN堆,它既简单又健壮,并且对插入和删除都有足够的效率(O(log N)时间复杂度);使用绝对时间(使用Linux中的CLOCK_MONOTONIC)作为事件的键。
注意,如果使用计时器事件作为超时,还需要确保取消事件是有效的。在正常操作中,超时是很少见的,所以像web服务器这样的东西很可能会取消它设置的所有超时,而不会真正触发任何超时。
等待下一个事件或另一个线程插入新计时器事件的线程
就我个人而言,我使用一个数组来保存事件的最小堆,由一个pthread_mutex_t保护,在添加新事件之后,其他线程用一个pthread_cond_t来发出信号。然后,使用pthread_cond_timedwait()来等待/睡眠指定的时间,或者直到线程通知新事件(以较早发生的为准)是一件简单的事情。
当下一个事件发生时(请注意,由于计划的原因,您可能会发现有多个单独的事件要发生,因此您可能根本不想睡觉(但您可能仍然会检查是否添加了新事件),则执行该事件。如果事件是周期性的,那么也可以将其重新插入到堆/队列中,以便下次启动。
选择事件的执行方式非常重要,而且是唯一真正棘手的一点。您可以使用标志——实际上从零切换到非零是安全的,即使更改不是原子的,只要您不依赖任何特定的非零值——;您可以使条件变量发出信号或广播;您可以发送信号量;您可以在特定线程中引发特定信号(如果安装了不带SA_RESTART标志的处理程序,即使是空信号处理程序也会导致阻塞I/O调用中断;我已经成功地将其用作I/O超时);如果使用gcc(或intel cc,pathscale,或波特兰C组编译器);等等。
如果需要调用特定的函数,我建议使用单独的线程(或者,如果应用程序/程序/游戏中的大部分工作是在这些计时器事件中完成的,则使用线程池)来执行事件。这使计时器线程简单而精确,同时使所有资源的使用易于控制。工作线程或线程池应该有一个受互斥锁和条件变量保护的事件fifo队列,这样计时器线程可以将每个事件添加到队列中,然后在条件变量上发出信号,通知(下一个)工作线程工作可用。
实际上,在我使用其他事件-动作模型的两个实例中,我现在相信Function Worker模型会更容易。特别是如果您让辅助函数接受由调用方定义的指针(指向结构),以便它们都具有相同的签名,那么这个接口将变得非常容易实现,但功能非常强大,用途非常广泛。
定时器线程加工作线程的方法有一个缺点,那就是(最小的)增加的延迟。工人线程不会在指定的时间得到工作,而是在稍后的一段时间。但是,如果您让工作线程获取当前时间,并将其与(未调整的)目标时间进行比较,并将其用作相应于目标时间之前触发事件的统计信息,则通常可以解决此问题。(我还没有验证,但我相信qt和gtk+工具包都会以类似的方式(如果不是相同的话)持续地估计这个延迟。)
问题

关于c - 如何在C中创建非单拍定时器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25752374/

相关文章:

c - 如何从 C 中的 STDIN 或 Unix 管道读取流

linux - 如何在 bash shell 中选择特定的行并在末尾回显

linux - 如何按顺序运行多个程序?

go - golang 中的可选超时

java - 预定定时器阻塞自身

SQL 按计时器重复事件

c++编程问题

c - 当无法更改数组的地址时,strcpy() 如何将字符串复制到数组?

c - 如何对齐由 ASCII 字符组成的表格中的数字

c++ - Ubuntu 12.04 中的 CUDA 链接错误