[英]C++ Timers in Unix
我们有一个处理事件计时器的API。 这个API表示它使用OS回调来处理定时事件(显然使用select())。
api也声明了这个执行顺序:可读事件可写事件计时器事件
这通过创建一个指向Timer对象的点来工作,但是将create函数传递给函数回调:
这些方面的东西:
Timer* theTimer = Timer::Event::create(timeInterval,&Thisclass::FunctionName);
我想知道这是如何工作的?
操作系统正在处理计时器本身,当它看到它触发时它是如何实际调用回调的? 回调是否在单独的执行线程中运行?
当我在回调函数(Thisclass :: FunctionName)中放入一个pthread_self()调用时,它看起来与创建游标的线程具有相同的线程ID! (非常困惑)
另外:上面的优先级列表是什么意思? 什么是可写事件vs可读事件vs计时器事件?
在这种情况下对select()的使用的任何解释也是值得赞赏的。
谢谢!
这看起来像是select(2)
的简单包装器。 该类保留了一个回调列表,我猜是分别用于读取,写入和计时器到期。 然后有一些类似于dispatch
或wait
调用的地方将文件描述符打包成集合,计算最小超时,并使用这些参数调用select
。 当select
返回时,包装器可能首先遍历读取集,调用读取回调,然后写入set,然后查看是否有任何定时器已过期并调用这些回调。 这可能发生在同一个线程上,也可能发生在不同的线程上,具体取决于包装器的实现。
你应该阅读select
和poll
- 它们非常方便。 一般术语是IO解复用 。
可读事件意味着数据可用于在不阻塞的情况下读取特定文件描述符,而可写事件意味着您可以在不阻塞的情况下写入特定文件描述符。 这些通常用于插座和管道。 有关这些内容的详细信息,请参见select()
手册页。
计时器事件意味着先前创建的计时器已过期。 如果库使用select()
或poll()
,则库本身必须跟踪定时器,因为这些函数接受单个超时。 库必须计算第一个计时器到期之前剩余的时间,并将其用于超时参数。 另一种方法是使用timer_create()
或较旧的变体(如setitimer()
或alarm()
通过信号接收通知。
您可以使用strace
(Linux)或truss
(Solaris)等工具确定在OS层使用的机制。 这些工具跟踪程序正在进行的实际系统调用。
猜测,对create()的调用将函数指针存储在某处。 然后,当计时器熄灭时,它会通过该指针调用您指定的函数。 但由于这不是标准C ++函数,您应该真正阅读文档或查看源代码以确定。
关于您的其他问题,我没有看到优先级列表的提及,而select()是一种通用事件多路复用器。
很可能有一个框架适用于典型的主循环,主循环的驱动力是选择调用。
select允许您等待文件描述符变为可读或可写(或者对于filedeescriptor上的“异常”)或者发生超时。 我猜这个库还允许你注册用于执行异步IO的回调,如果它是一个GUI库,它将通过unix上的文件描述符获得低原始GUI事件。
要在这样的循环中实现计时器回调,您只需保留计时器的优先级队列,并在选择超时或filedescriptor事件上处理它们。
优先级意味着它在定时器之前处理文件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
}
由于计时器回调中的线程id与创建者线程相同,我认为它是以某种方式使用信号实现的。
当一个信号被发送到一个线程时,线程的状态被保存并且信号处理程序被调用然后调用事件回调。 因此,处理程序在创建者线程中被调用,该线程在信号处理程序返回之前被中断。
也许另一个线程使用select()等待所有计时器,如果计时器到期,它会向创建过期计时器的线程发送一个信号。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.