【问题标题】:How to implement an asynchronous timer on a *nix system using pthreads如何使用 pthreads 在 *nix 系统上实现异步计时器
【发布时间】:2009-08-24 13:35:19
【问题描述】:

我有两个问题:

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 只等待一组文件描述符,但我想要一个按其到期超时升序排列的计时器列表,并希望 select 告诉我第一个计时器何时到期等等。所以问题归结为可以使用选择/轮询或其他事件多路分解器来实现异步计时器吗?

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

  1. 锁定互斥体
  2. 检查第一个计时器的时间
  3. 条件定时等待该时间(如果其他线程插入一个到期时间小于第一个计时器的计时器,则唤醒)条件等待解锁锁。
  4. 条件等待结束后,我们获得了锁。所以解锁它,从堆中移除计时器并调用回调函数。
  5. 去1

我想要这种异步定时器的时间复杂度。据我所见

  1. 插入为 lg(n)
  2. 有效期为 lg(n)
  3. 取消

:(这让我头晕目眩)问题是我根据他们的时间有一个最小的定时器堆,当我插入一个定时器时,我得到一个唯一的 id。所以当我需要取消定时器时,我需要提供这个定时器 id 并且在堆中搜索这个定时器 id 最坏的情况是 O(n)

我错了吗?

可以在 O(lg n) 内完成取消

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

【问题讨论】:

  • 你在哪个平台上写这个?例如,Windows 有一些 API 调用允许单线程应用程序使用 Timer。还是您想编写自己的操作系统?
  • 大家好,我正在研究 *nix 系统,即任何 posix 兼容系统 linux、solaris 和其他 unice。我正在使用 pthreads。

标签: multithreading unix pthreads


【解决方案1】:

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

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

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

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

【讨论】:

    【解决方案2】:
    man pthread_cond_timedwait
    
    man pthread_cond_signal
    

    【讨论】:

      【解决方案3】:

      如果您是 Windows 应用程序,您可以在将来的某个时间触发 WM_TIMER 消息发送给您,即使您的应用程序是单线程的,它也可以工作。但是,计时的准确性不会很高。

      如果您的应用在恒定循环中运行(如游戏,以 60Hz 渲染),您可以简单地检查循环中的每个时间,看看是否需要调用触发事件。

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

      【讨论】:

      • 您在此处提供的第二个选项实际上是可能的,但实施起来非常痛苦:)
      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-09-09
        • 2012-11-01
        • 2017-04-21
        • 1970-01-01
        • 1970-01-01
        • 2013-05-19
        • 1970-01-01
        相关资源
        最近更新 更多