(转)live555学习笔记-UsageEnvironment和TaskScheduler
2011-12-6阅读1264 评论1
一直想学习流媒体服务器的设计,这几天有点时间,看了一下live555的源代码。live555是一个开源的跨平台流媒体服务器,使用编程语言是C++。将现阶段学习笔记总结如下,其实关键是要弄明白几个类的作用和它们之间的关系:
一.UsageEnvironment类以及其派生类的继承关系
基类UsageEnvironment是一个抽象类,它主要是定义了一些接口函数(纯虚函数)包括错误代码/结果消息系列函数,重载输出操作符系列函数等;以及定义重要的数据成员fScheduler,它声明为TaskScheduler的引用;它重要是因为它是整个程序运作的引擎。为了做更好的封装UsageEnvironment将构造函数和析构函数声明为protected。后面会看到,UsageEnvironment的派生类的构造是通过静态函数creatNew实现的,而析构都是通过调用的reClain()函数实现。
BasicUsageEnvironment0很简单,它定义了一个字符数组来存储结果消息,只是实现了UsageEnvironment中的结果消息系列函数,所以它仍然是一个抽象类。
BasicUsageEnvironment也很简单,它实现了输出操作符系列函数的重载以及静态函数creatNew。
因此只需要记住这三样东西:结果消息函数、输出操作符函数、TaskScheduler引用变量,便可以了解UsageEnvironment及其派生类的作用。实际上完全可以分开三个类来表示,作者可能是为了方便使用才把它们放到一起。因为当我们要给程序增加功能时,可以在TaskScheduler完成,而想设置和输出程序运行期间的消息,可以直接使用输出操作符和结果消息处理函数,它给后续程序开发提供一个很好的环境。
二.TaskScheduler类以及其派生类的继承关系
顾名思义,TaskScheduler是一个任务调度器。它的继承关系图跟UsageEnvironment类似,呵呵,有了前面的分析我们也应该很容易掌握这个类。这里的任务是抽象的,可以想象为一段代码或一个函数,任务调度目的就是要决定程序当前应该运行哪一个任务。
1.TaskScheduler是一个抽象基类,它定义了一系列的接口函数,其中doEventLoop定义为程序循环函数。根据任务的类别,作者分成三类的来处理,每一次循环都会按照下面顺序来完成调用(请参考BasicTaskScheduler中的singleStep函数):
(1)首先处理的是Socket Event,负责I/O复用,使用select函数等待指定的描述字准备好读、写或有异常条件处理。若select返回值大于-1,则转到相应的处理函数;否则表明发生异常,程序将转到错误处理代码中去。该类型适合于有I/O操作的任务。
相关函数:setBackgroundHandling/disableBackgroundHandling/moveSocketHandling
(2)接着是处理触发器事件(Trigger-Event)。作者定义了一个32位的位图来实现触发事件,当某一位设置为1则表明要触发该位对应的事件。若同时有多个(3个或以上)触发事件,它们触发的先后还会跟事件创建的先后有关,因此这一类型仅适合于没有顺序依赖关系的任务。
相关函数:createEventTrigger/deleteEventTrigger/triggerEvent
(3)最后一个是延迟任务(Delayed Task),它是一个带有时间的任务。当剩余时间不为0,则任务不执行。通过调整任务的剩余时间,可以灵活地安排任务。
相关函数:scheduleDelayedTask/unscheduleDelayedTask/rescheduleDelayedTask
TaskScheduler为了兼容以前的程序,还保留了turnOnBackgroundReadHandling/turnOffBackgroundReadHandling函数 ,实际上它们也是通过调用setBackgroundHandling/disableBackgroundHandling实现的。当然还有一个错误处理函数interalError,处理程序错误,派生类可重载。
2.BasicTaskScheduler0主要是实现了触发事件和延迟任务。
(1)触发事件是通一个32位图实现的,它利用两个数组存储存储触发事件的函数指针和函数参数指针。它是从最高为开始存放的,即最高位对应函数指针数组和参数指针数组的第0个元素,最多可使用32个触发器。它还保存上一次的触发的序号和触发mask,作为下一次起始点,从而保证所有的触发器都能够触发。
(2)延迟任务是通过一个双向循环链表实现的。它的节点实际是AlarmHandler,而链表则实现为DelayQueue,它们都是从DelayQueueEntry基类继承得到的,三者间的关系如下图:
DelayQueueEntry可看作是一个抽象的双向链表中的节点,除了前向指针和后向指针,它附加了一个fDeltaTimeRemaining成员和fToken成员,表示延时时间和标识节点的唯一标志。此外还定义了有一个TimeOut时调用的虚函数handleTimeOut()。
AlarmHandler是实际的延时任务节点,非常简单的,它在DelayQueueEntry基础上定义了一个的函数指针和函数参数指针,并重载了handleTimeOut函数。
DelayQueue是作为循环链表类,一般来说不用继承DelayQueueEntry,作者在这里是把它作为循环链表的头节点。其余的都是循环链表的常规操作,包括节点的查询、插入、删除、更新操作。DelayQueue还实现了timeToNextAlarm()返回头节点的时延,以及handleAlarm()实际延时任务处理,实际调用的是节点的handleTimeOut()函数;
3.BasicTaskScheduler类实现剩下的I/O操作任务接口和三类任务的实际调度(singleStep函数)。
I/O任务的实现也很简单的,它也是使用双向循环链表来存储任务。它的节点定义为HandlerDiscriptor,包括前向后向节点指针,函数指针和函数参数指针,以及IO相关的socketNum和conditionSet数据成员。循环链表类定义为HandlerSet,类似地也定义了查询、插入、删除、更新操作。它声明了一个节点成员作为头节点。
三.对UsageEnvironment的测试
// This is a test for basic objects, such as TaskSchduler, UsageEnvironment and
// so on. It's just for study purpose. #include <BasicUsageEnvironment.hh>
#include <iostream>
using namespace std; TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler); void taskFunc(void* clientData){
cout<<"taskFunc(\""<<(char*)clientData<<"\") called."<<endl;
} void handlerFunc(void* clientData, int mask){
cout<<"handlerFunc(\""<<(char*)clientData<<"\", "<<mask
<<") called."<<endl;
scheduler->disableBackgroundHandling(STDOUT_FILENO);
} int main(int argc, char* args[])
{
// IO event test
char handlerClientData[] = "IO Event";
scheduler->setBackgroundHandling(STDOUT_FILENO, SOCKET_WRITABLE,
(TaskScheduler::BackgroundHandlerProc*)&handlerFunc, handlerClientData); // trigger event test
EventTriggerId id1 = scheduler->createEventTrigger(taskFunc);
char triggerClientData1[] = "Trigger Event 1";
EventTriggerId id2 = scheduler->createEventTrigger(taskFunc);
char triggerClientData2[] = "Trigger Event 2";
EventTriggerId id3 = scheduler->createEventTrigger(taskFunc);
char triggerClientData3[] = "Trigger Event 3";
(*env)<<"Setting Event triggers...\n";
scheduler->triggerEvent(id2, (void*)triggerClientData2);
scheduler->triggerEvent(id1, (void*)triggerClientData1);
scheduler->triggerEvent(id3, (void*)triggerClientData3);
(*env)<<"Event triggers has been set.\n"; // delayed task test
char delayedTaskClientData1[] = "Delayed Task 1s";
TaskToken token1 = scheduler->scheduleDelayedTask(1000000,
taskFunc, delayedTaskClientData1);
char delayedTaskClientData2[] = "Delayed Task 5s";
TaskToken token2 = scheduler->scheduleDelayedTask(5000000,
taskFunc, delayedTaskClientData2); // loop
scheduler->doEventLoop(); return 0;
}
Event triggers has been set.
handlerFunc("IO Event", 4) called.
taskFunc("Trigger Event 1") called.
taskFunc("Trigger Event 2") called.
taskFunc("Trigger Event 3") called.
taskFunc("Delayed Task 1s") called.
taskFunc("Delayed Task 5s") called.
总结:
live555使用UsageEvironment类和TaskSheduler类以及它们的派生类建立一个良好的程序开发基本架构,在此基础上可以方便构建我们的各种应用。一个任务只需要两步就可以完成,先根据任务的类型定义任务处理函数,然后将任务添加到循环体里面即可。
值得注意的是这个任务调度器的性能和线程安全问题。如果某个任务需要长时间的处理或发生阻塞,那么主循环也将发生阻塞,其他的任务将得不到及时的响应,因此设计任务时要考虑任务花费的时间,若太长则要考虑是否开辟另外一个线程来处理了。另外,从源代码上来看,该任务调度器并没有为支持多线程做更多的工作,所以通过多个线程添加任务,可能会发生异常。
转自:http://m.blog.csdn.net/blog/huangwanzhang/7042843
(转)live555学习笔记-UsageEnvironment和TaskScheduler的更多相关文章
- (转)live555学习笔记10-h264 RTP传输详解(2)
参考: 1,live555学习笔记10-h264 RTP传输详解(2) http://blog.csdn.net/niu_gao/article/details/6936108 2,H264 sps ...
- (转)live555学习笔记9-h264 RTP传输详解(1)
九 h264 RTP传输详解(1) 前几章对Server端的介绍中有个比较重要的问题没有仔细探究:如何打开文件并获得其SDP信息.我们就从这里入手吧. 当RTSPServer收到对某个媒体的DESCR ...
- (转)live555学习笔记7-RTP打包与发送
七 RTP打包与发送 rtp传送开始于函数:MediaSink::startPlaying().想想也有道理,应是sink跟source要数据,所以从sink上调用startplaying(嘿嘿,相当 ...
- live555源码学习笔记之TaskScheduler
今天抽空研究了下live555的任务实现: TaskScheduler分为三种任务:socket handler,event handler,delay task.这三种任务的特点是,前两个加入执行队 ...
- 开源项目live555学习心得
推荐:伊朗美女找丈夫比找工作难女人婚前一定要看清三件事 × 登录注册 疯狂少男-IT技术的博客 http://blog.sina.com.cn/crazyboyzhaolei [订阅][手机订 ...
- 【WPF】学习笔记(三)——这个家伙跟电子签名板有个约定
这篇博客依旧是以电子签名板为基础而展开的,主要是对前文([WPF]学习笔记(一)--做一个简单的电子签名板)存在的部分问题进行解释,以及部分小功能的添加.由于这篇博客是建立在学习笔记一的基础上的,所以 ...
- live555学习(一)通读Makefile编译live555
live555学习(一)通读Makefile编译live555 live555 编译live555 学习开源 live555学习(一)通读Makefile编译live555 前言 live555简介 ...
- 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性
基于.net的分布式系统限流组件 在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...
- Learning hard 学习笔记
第一章 你真的了解C#吗 1.什么是C#, 微软公司,面向对象,运行于.NET Framework之上, 2.C#能编写哪些应用程序, Windows应用桌面程序,Web应用程序,Web服务, 3.什 ...
随机推荐
- C++11 constexpr使用
C++11为了提高代码执行效率做了一些改善.这种改善之一就是:生成常量表达式,允许程序利用编译时的计算能力.假如你熟悉模板元编程,你将发现constexpr使这一切变得更加简单.constexpr使我 ...
- 关于ddx/ddy重建法线在edge边沿上的artifacts问题
经验证,原来ddx/ddy这两个操作,在forward rendering与deferred rendering中存在着微妙的应用区别. 在forward rendering中,GPU shader会 ...
- 【C/C++】标准IO操作
fwrite实现文件复制 //@ author 成鹏致远 //@ net http://infodown.tap.cn //@ qq 552158509 //@ blog lcw.cnblogs.co ...
- 【转】一件有趣的事:我用 Python 爬了爬自己的微信朋友
偶然了解到 Python 里的 itchat 包,它已经完成了 wechat 的个人账号 API 接口,使爬取个人微信信息更加方便. 于是乎玩心一起,打算爬一下自己的微信. 步骤核心: 网页启动not ...
- js用img代替ajax js心跳 向服务器定时传送参数 主要计算用户在线时长
html: <!doctype html><html><head><meta charset="utf-8"><title&g ...
- csv和excel互转
Python csv转换为excel学习笔记: openpyxl模块需要安装pip install openpyxl import openpyxl import csv '''读取csv文件写入ex ...
- linux清理Java环境
1.清理Java环境rm -f /usr/bin/javarm -f /etc/alternatives/java rm -f /usr/bin/javacrm -f /etc/alternative ...
- app测试初窥
要用到的两个神器:abd&drozer adb介绍 adb的全称为Android Debug Bridge,就是起到调试桥的作用,作为一名开发者倒是常用到这个工具.借助adb工具,我们可以管理 ...
- Drozer快速使用指南
1.简介: Drozer是一款用于测试android应用程序漏洞的安全评估工具,能够发现多种类型的安全的漏洞,免费版本的相关资源下载地址: https://www.mwrinfosecurity.co ...
- Git CMD连接,管理(remote,add,commit,push)github repository
git initmd testcd testgit statusgit add test //git add test/a.txtgit status git remote add origin g ...