代码之家  ›  专栏  ›  技术社区  ›  Alex

UNIX中的C++定时器

  •  3
  • Alex  · 技术社区  · 17 年前

    我们有一个处理事件计时器的API。这个API说它使用操作系统回调来处理定时事件(显然是使用select())。

    API还声明执行顺序: 可读事件 可写事件 定时器事件

    这是通过创建指向计时器对象的点,但将创建函数传递给函数回调来实现的:

    沿着这些线的东西:

    Timer* theTimer =  Timer::Event::create(timeInterval,&Thisclass::FunctionName);  
    

    我想知道这是怎么回事?
    操作系统正在处理计时器本身,当它看到它触发时,它实际上是如何调用回调的?回调是否在单独的执行线程中运行?

    当我将pthread_self()调用放入回调函数(thisclass::functionname)中时,它似乎与创建计时器的线程具有相同的线程ID!(对此非常困惑)

    还有:上面的优先级列表是什么意思?什么是可写事件、可读事件和计时器事件?

    对于在这个场景中使用select()的任何解释,我们都将不胜感激。

    谢谢!

    5 回复  |  直到 14 年前
        1
  •  2
  •   Nikolai Fetissov    17 年前

    这看起来像一个简单的包装 select(2) . 这个类保存了一个回调列表,我想对于读、写和计时器过期是分开的。然后有一个像 dispatch wait 在那里调用,将给定的文件描述符打包成集合,计算最小超时,并调用 select 有了这些论据。什么时候? 选择 返回,包装器可能首先遍历读取集,调用读取回调,然后调用写入集,然后查看是否有任何计时器已过期,并调用这些回调。这可能发生在同一个线程上,也可能发生在不同的线程上,具体取决于包装器的实现。

    你应该继续读下去 选择 poll -它们非常方便。 一般术语是 IO解复用 .

        2
  •  2
  •   alanc    14 年前

    可读事件意味着可以在不阻塞的情况下读取特定文件描述符上的数据,而可写事件意味着您可以在不阻塞的情况下写入特定文件描述符。这些通常用于插座和管道。查看 select() 有关这些的详细信息,请参阅手册页。

    计时器事件表示先前创建的计时器已过期。如果库正在使用 选择() poll() ,因为这些函数接受单个超时,所以库本身必须跟踪计时器。库必须计算第一个计时器到期前的剩余时间,并将其用于超时参数。另一种方法是 timer_create() 或类似的旧变体 setitimer() alarm() 通过信号接收通知。

    您可以使用类似于 strace (Linux)或 truss (索拉里斯)这些工具跟踪程序正在进行的实际系统调用。

        3
  •  1
  •   anon    17 年前

    猜测一下,create()调用将函数指针存储在某个地方。然后,当计时器关闭时,它调用通过该指针指定的函数。但是,由于这不是一个标准的C++函数,所以您应该真正阅读文档或查看源代码来确定。

    关于您的其他问题,我没有看到提到优先级列表,select()是一种通用事件多路复用器。

        4
  •  1
  •   nos    17 年前

    很可能有一个框架与典型的主循环一起工作,主循环的驱动力是select调用。

    选择允许您等待一个文件描述符变为可读或可写(或文件编辑器上的“异常”)或发生超时。我猜这个库还允许您注册用于异步IO的回调,如果它是一个GUI库,它将通过unix上的文件描述符获得低基元的GUI事件。

    要在这样的循环中实现计时器回调,只需保留计时器的优先级队列,并在选择超时或文件描述符事件时处理它们。

    优先级意味着它在计时器之前处理文件I/O,这本身需要时间,可能导致GUI更新,最终导致运行GUI事件处理程序,或其他花费时间维护I/O的任务。

    图书馆或多或少在做

    for(;;) {
      timeout = calculate_min_timeout();
      ret = select(...,timeout); //wait for a timeout event or filedescriptor events
      if(ret > 0) {
        process_readable_descriptors();
        process_writable_descriptors();
      }
      process_timer_queue();  //scan through a timer priority queue and invoke callbacks
    }  
    
        5
  •  0
  •   mmmmmmmm    17 年前

    由于计时器回调中的线程ID与创建者线程相同,我认为它是通过某种方式使用信号实现的。

    当一个信号被发送到一个线程时,该线程的状态被保存,并调用信号处理程序,然后调用事件回调。 因此,在创建者线程中调用该处理程序,该线程被中断,直到信号处理程序返回。

    可能另一个线程使用select()等待所有计时器,如果计时器过期,它会向创建过期计时器的线程发送一个信号。