(转)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.什 ...
随机推荐
- 使用 "java -jar"命令启动jar包时报不支持的jdk版本异常
在使用java -jar 命令启动一个jar包的时候,提示版本不支持: [root@hadoop01 eureka-server-master]# java -jar xuebusi-eureka-s ...
- sql server 与oracle 中字段类型的对应
SqlServer 2k转换为Oracle 10g 列名 SqlServer数据类型 SqlServer长度 Oracle数据类型 column1 ) column2 ) column3 ) colu ...
- LeetCode: String to Integer (atoi) 解题报告
String to Integer (atoi) Implement atoi to convert a string to an integer. Hint: Carefully consider ...
- 【Linux】利用Xvfb关闭chrome的图形化输出
利用Xvfb关闭chrome的图形化输出 #!/bin/bash . /home/fzuir/.profile # JAVA export JAVA_HOME=/usr/local/jdk1.7.0_ ...
- svn 版本管理与自动部分发布
作为团队开发项目时,会遇到项目的版本管理,测试部署与发布部署,下面是摘至他人的关于版本管理和自动部署的方案. svn自动部署的实现: 使用svn的hook功能 1.在版本库的hooks目录下面,有一些 ...
- whatweb wordpress.rb
## # This file is part of WhatWeb and may be subject to # redistribution and commercial restrictions ...
- JAVA-JSP内置对象之response对象实现页面跳转
相关资料:<21天学通Java Web开发> response对象 实现页面跳转1.可以通过response对象的sendRedirect()方法设置页面重定向,从而实现页面跳转.2.这种 ...
- 插入中国所有省和市的SQL语句--以后用
需要做根据省市过滤查询的,不得已写入数据库,这个留着方便以后用. 首先创建两张表 drop database if exists BSM; create database BSM; use BSM; ...
- 纯css3开发的响应式设计动画菜单(支持ie8)
这是一个响应式设计的菜单.单击列表图标,当你显示屏大小可以完全水平放下所有菜单项时,菜单水平显示(如图1).当你的显示屏不能水平放置所有菜单项时,菜单垂直显示(如图2). 而且显示的时候是以动画的型式 ...
- c# dump 程序崩溃 windbg
待研究 http://issf.blog.163.com/blog/static/194129082201002534895/ http://www.cppblog.com/woaidongmao/a ...