如何使用pthreads在* nix系统上实现异步计时器

时间:2009-08-24 13:35:20

标签: multithreading unix pthreads

我有两个问题:

Q1)我可以在单线程应用程序中实现异步计时器,即我想要这样的功能。

....
Timer mytimer(5,timeOutHandler)


.... //this thread is doing some other task


...
and after 5 seconds, the timeOutHandler function is invoked.

据我所知,单个线程应用程序无法做到这一点(如果我错了,请纠正我)。我不知道是否可以使用select作为解复用器来完成,但即使可以使用select,事件循环也需要一个线程?不是吗?

我还想知道我是否可以使用select实现定时器(不是超时)。 选择仅等待文件描述符集,但我希望按照到期超时的升序列出计时器列表,并希望选择在第一个计时器到期时告诉我,依此类推。所以问题归结为可以使用select / poll或其他一些事件多路分解器实现异步定时器吗?

Q2)现在让我们来谈谈我的第二个问题。 这是我的主要问题。 现在我使用一个专用线程来检查超时,即我有一小组计时器(到期时间),这个线程休眠直到第一个计时器到期,然后调用回调。 代码看起来像这样

  1. 锁定互斥锁
  2. 检查第一个计时器的时间
  3. 条件timed等待该时间(如果某个其他线程插入一个到期时间小于第一个定时器的定时器,则唤醒)条件等待解锁。
  4. 条件等待结束后我们有锁。因此解锁它,从堆中删除计时器并调用回调函数。
  5. 转到1
  6. 我想要这种异步计时器的时间复杂度。从我看到的

    1. 插入是lg(n)
    2. 到期时间为lg(n)
    3. 取消
    4. :(这是让我头晕的问题)问题是我根据他们的时间有一小组计时器,当我插入一个计时器时,我得到一个唯一的ID。所以当我需要取消定时器时,我需要提供这个定时器ID并在堆中搜索这个定时器id将在最坏的情况下采取O(n)

      我错了吗?

      可以在O(lg n)

      中取消

      请注意一些多线程问题。一旦得到一些回复,我会详细说明我的上一句话的意思。

4 个答案:

答案 0 :(得分:2)

如果我们可以假设线程将大部分时间用在select()中,那么使用单个线程实现定时器绝对是可能的(通常更可取)。

您可以使用signal()和SIGALRM检查以实现POSIX下的功能,但我建议不要使用它(Unix信号是丑陋的黑客,当信号回调函数运行时,你可以在里面做很少的事情它安全,因为它与您的应用程序线程异步运行)

关于使用select()的超时来实现计时器功能的想法很好 - 这是一种非常常见的技术,它运行良好。基本上,您保留按时间戳排序的待处理/即将发生的事件列表,并在调用select()之前从列表中的第一个时间戳中减去当前时间,并将该时间增量作为超时值传递给选择()。 (注意:如果时间增量为负,则传递零作为超时值!)当select()返回时,将当前时间与列表中第一项的时间进行比较;如果当前时间大于或等于事件时间,则处理计时器事件,弹出列表头部的第一个项目,然后重复。

至于效率,你的大O时间将完全取决于用于存储有序定时器列表的数据结构。如果使用优先级队列(或类似的有序树类型结构),则所有操作都可以有O(log N)次。您甚至可以进一步将事件列表存储在哈希表(键入事件ID)和链表(按时间戳排序)中,并且可以为所有操作提供O(1)次。但是,O(log N)可能足够高效,除非您计划同时有大量事件待定。

答案 1 :(得分:1)

man pthread_cond_timedwait

man pthread_cond_signal

答案 2 :(得分:0)

如果您是一个Windows应用程序,您可以触发WM_TIMER消息,以便在将来的某个时间点发送给您,即使您的应用程序是单线程的,这也会有效。但是,时间的准确性不会很好。

如果您的应用程序以恒定循环运行(如游戏,以60Hz渲染),您只需在循环周围检查每次是否需要调用触发事件。

如果您希望您的应用程序基本上被中断,您的函数被调用,然后执行返回原来的位置,那么您可能会运气不佳。

答案 3 :(得分:0)

如果您正在使用C#,System.Timers.Timer将执行您想要的操作。您指定计时器在到期时调用的事件处理程序方法,该方法可以位于您从中调用计时器的类中。请注意,当计时器调用事件处理程序时,它将在单独的线程上执行此操作,如果您正在更新用户界面,则需要考虑该线程,或使用其SynchronizingObject属性在UI线程上运行它。