简析LIVE555中的延时队列

最近在看LIVE555的源码,对其中的延时队列感觉有点乱,网上查询资料,于是就总结一下。

首先描述一下LIVE555中的延时队列的设计理念。如下图,A,B,C分别为时间轴上的三个事件点,而head表示当前时间点。

要描述一个事件发生的时间,通常可以有两种方法:一种方法直接描述事件发生的绝对时间;另一种方法则是可以描述和另一事件发生的相对时间。而LIVE555中采用的就是后者。  

在LIVE555中,首先将所有的事件点以发生时间的先后进行排序,然后每个事件对应的时间都是相对于前一事件发生的时间差。比如B事件中存储的时间就是A事件触发后,再去触发B事件所需要的时间。这样,我们每次去查询这个队列中是否有事件被触发的时候,就只需要查询整个队列中的第一个事件就可以了。

 然后就是LIVE555中的实现方法了。整个延时队列是用DelayQueue这个类实现的,而它的基类DelayQueueEntry就是用来描述每个事件节点的。在DelayQueueEntry中的主要成员有以下几个:fDelayTimeRemaining表示的就是与前一事件之间的时间差;fNext和fPrev就是指向时间轴上的下一个事件和前一个事件的指针;ftoken表示当前节点的标识;handleTimeout就是事件超时后的处理方法。

而DelayQueue类里描述的则是具体的实现方法。首先是一些对这个队列进行的基本操作:addEntry实现的是在队列中增加事件节点;removeEntry实现的是在队列中删除某事件节点;updateEntry实现的则是更新某事件的触发时间;而findEntryByToken则是根据节点的标识查找相应的事件。在此类中最常用的方法应该是synchronize,它实现的就是将整个事件队列和当前系统时间进行同步,检测有无事件已经被触发,如果触发并调用handleAlarm方法对相应事件进行处理。而属性fLastSyncTime表示的就是上次同步的系统时间,其实一般情况下,方法synchronize的实现方法其实就是简单地把队列上第一个事件节点存储的时间差减去当前系统时间和上次同步时间的差。

附:相关类结构:

=================================================================

==> 相关typedef定义

typedef void TaskFunc(void* clientData);

typedef void* TaskToken;

// 下面Timeval类有涉及

#ifdef TIME_BASE

typedef TIME_BASE time_base_seconds;

#else

typedef long time_base_seconds;

#endif

==> 相关类的说明(由于有些类很大,故不会完整贴出,故用说明)

///// A "Timeval" can be either an absolute time, or a time interval /////

class Timeval {

public:

  time_base_seconds seconds() const {

    return fTv.tv_sec;

  }

  time_base_seconds seconds() {

    return fTv.tv_sec;

  }

  time_base_seconds useconds() const {

return fTv.tv_usec;

  }

int operator>=(Timeval const& arg2) const; // 以>=为基础,推算出其余条件判断(<=、<</span>、>等)的真假

  int operator<=(Timeval const& arg2) const {

    return arg2 >= *this;

  }

  int operator<</b>(Timeval const& arg2) const {

    return !(*this >= arg2);

  } 

int operator>(Timeval const& arg2) const {

    return arg2 < *this;

  }

  int operator==(Timeval const& arg2) const {

    return *this >= arg2 && arg2 >= *this;

  }

  int operator!=(Timeval const& arg2) const {

    return !(*this == arg2);

  }

  void operator+=(class DelayInterval const& arg2);

  void operator-=(class DelayInterval const& arg2);

  // returns ZERO iff arg2 >= arg1

protected:

  Timeval_r(time_base_seconds seconds, time_base_seconds useconds) {

    fTv.tv_sec = seconds; fTv.tv_usec = useconds;

  }

private:

  time_base_seconds& secs() {

    return (time_base_seconds&)fTv.tv_sec;

  }

  time_base_seconds& usecs() {

    return (time_base_seconds&)fTv.tv_usec;

  }

  struct timeval fTv; // 看到,所有的所有,其实是在为timeval这个结构体封装了一系列操作函数

};

++++++++++++++++++++++++++++++++++++++++++

// 下面这个类用以处理自1970年1月1日以来的绝对时间

class EventTime: public Timeval {

public:

  EventTime(unsigned secondsSinceEpoch = 0,

    unsigned usecondsSinceEpoch = 0)

    // We use the Unix standard epoch: January 1, 1970

    : Timeval_r(secondsSinceEpoch, usecondsSinceEpoch) {}

};

class DelayQueueEntry { // 通过它来链接所有的事件信息,组成队列(见下面DelayQueue类)

public:

  virtual ~DelayQueueEntry();

  intptr_t token() {

    return fToken;

  }

protected: // abstract base class

  DelayQueueEntry(DelayInterval delay);

  virtual void handleTimeout();

private:

  friend class DelayQueue;

  DelayQueueEntry* fNext;

  DelayQueueEntry* fPrev;

  DelayInterval fDeltaTimeRemaining;

  intptr_t fToken;

  static intptr_t tokenCounter;

};

class DelayQueue: public DelayQueueEntry {

public:

  DelayQueue();

  virtual ~DelayQueue();

  void addEntry(DelayQueueEntry* newEntry); // returns a token for the entry

  void updateEntry(DelayQueueEntry* entry, DelayInterval newDelay);

  void updateEntry(intptr_t tokenToFind, DelayInterval newDelay);

  void removeEntry(DelayQueueEntry* entry); // but doesn't delete it

  DelayQueueEntry* removeEntry(intptr_t tokenToFind); // but doesn't delete it

  DelayInterval const& timeToNextAlarm();

  void handleAlarm();

private:

  DelayQueueEntry* head() { return fNext; } // 返回DelayQueueEntry类中的fNext队头成员

  DelayQueueEntry* findEntryByToken(intptr_t token);

  void synchronize(); // bring the 'time remaining' fields up-to-date

  EventTime fLastSyncTime;

};

////////// A subclass of DelayQueueEntry,

//////////     used to implement BasicTaskScheduler0::scheduleDelayedTask()

class AlarmHandler: public DelayQueueEntry {

public:

  AlarmHandler(TaskFunc* proc, void* clientData, DelayInterval timeToDelay)

    : DelayQueueEntry(timeToDelay), fProc(proc), fClientData(clientData) {

  }

private: // redefined virtual functions

  virtual void handleTimeout() {

    (*fProc)(fClientData);

    DelayQueueEntry::handleTimeout();

  }

private:

  TaskFunc* fProc;

  void* fClientData;

};

时间: 2024-10-06 12:58:54

简析LIVE555中的延时队列的相关文章

简析Linux中如何改变文件或目录的访问权限_unix linux

Linux系统中的每个文件和目录都有访问许可权限,用它来确定谁可以通过何种方式对文件和目录进行访问和操作. 文件或目录的访问权限分为只读,只写和可执行三种.以文件为例,只读权限表示只允许读其内容,而禁止对其做任何的更改操作.可执行权限表示允许将该文件作为一个程序执行.文件被创建时,文件所有者自动拥有对该文件的读.写和可执行权限,以便于对文件的阅读和修改.用户也可根据需要把访问权限设置为需要的任何组合. 有三种不同类型的用户可对文件或目录进行访问:文件所有者,同组用户.其他用户.所有者一般是文件的

简析项目中常用的七参数转换法和四参数转换法以及涉及到的基本测量学知识

文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 在了解这两种转换方法时,我们有必要先了解一些与此相关的基本知识.我们有三种常用的方式来表示空间坐标,分别是:经纬度和高层.平面坐标和高层以及空间直角坐标. 2.经纬度坐标系(大地坐标系) 这里我首先要强调:天文坐标表示的经纬度和大地坐标系表示的经纬度是不同的.所以,同一个经纬度数值,在BJ54和WGS84下表示的是不同的位置,而以下我说的经纬度均指大地坐标系

简析Geoserver中获取图层列表以及各图层描述信息的三种方法

文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/. 1.背景 实际项目中需要获取到Geoserver中的图层组织以及各图层的描述信息:比如字段列表等.在AGS中,我们可以直接通过其提供的REST服务获取到图层组织情况以及图层详细信息列表,具体如下所示:   那么在Geoserver中是否也有相关用法?各种方法之间有何优劣? 2.REST请求方法 2.1方法描述 该方法与上面讲解的AGS的REST请求方法类似,也是先获取

Java多线程中的延时队列DelayQueue

慢慢进入JAVA的内心世界, 今天也一直在和JAVA的语法作斗争, 到周三,写的一个基于SPRING BOOT的日志小模块, 成型啦~~ 关于DelayQueue,书上是这样说的: Student.java package demo.thread; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; public class Student implements Delayed{ private S

简析JAVA的XML编程

xml|编程  个人认为这篇文章通俗易懂,值得推荐.    XML作为全球通用的结构化语言,越来越受人们青睐,各种开发平台(比如Microsoft Studio系列.Oracle系列.Inprise Borland系列等)也都把支持XML开发作为宣传口号之一 .由于笔者所从事的电子政务开发较早的引入了XML,所以尝到了许多甜头,在许多项目中利用XML数据交换信息,省去了许多麻烦事,不用制定繁锁的数据格式,利用XML数据易于表达,也利于一线开发者跟踪调试.         笔者先前也曾发表过相关的

android:descendantFocusability用法简析

android:descendantFocusability用法简析     开发中很常见的一个问题,项目中的listview不仅仅是简单的文字,常常需要自己定义listview,自己的Adapter去继承BaseAdapter,在adapter中按照需求进行编写,问题就出现了,可能会发生点击每一个item的时候没有反应,无法获取的焦点.原因多半是由于在你自己定义的Item中存在诸如ImageButton,Button,CheckBox等子控件(也可以说是Button或者Checkable的子类

Rails系统中的AJAX开发技术简析(2)

ajax|rails 五. 使用link_to_remote Rails有若干帮助者方法以在你的视图的模板中实现Ajax.一种最简单且很通用的方法就是link_to_remote().让我们考察一个简单的web页面-它实现询问时间并且有一个链接,用户可以点击这个链接来获得当前的时间.该应用程序经由link_to_remote()使用Ajax以检索时间并且显示它于web页面. 我的视图模板(index.rhtml)看起来象: <html><head><title>Ajax

Rails系统中的AJAX开发技术简析(3)

ajax|rails 六. 使用form_remote_tag 这个form_remote_tag()帮助函数与link_to_remote()很相似,除了它也发送一个HTML表单的内容之外.这意味着该行动处理器可以使用用户输入的数据来形成响应.这个实例显示了一个web页面-它有一个列表和一个支持Ajax的表单-该表单能够让用户添加一些选项到该列表中. 我的视图模板(index.rhtml)看上去象: <html><head><title>Ajax List Demo&

WebGL 中 OpenGL ES 指令与 iOS 中 C 版指令的差异简析

WebGL 中 OpenGL ES 指令与 iOS 中 C 版指令的差异简析 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. WebGL 中 OpenGL ES 指令与 iOS 中 C 版指令的