(转)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.什 ...
随机推荐
- python(39):argparse的用法,从外部传入指定参数
直接上例子: # /usr/bin/env python # coding=utf8 import os import argparse import logging import sys FORMA ...
- iOS开发-Tom猫
// // ViewController.m // 20-tom猫 // // Created by hongqiangli on 2017/8/1. // Copyright © 李洪强. ...
- ImportError: cannot import name 'main'的解决办法
一.现象 使用pip出现如下提示: Traceback (most recent call last): File "/usr/bin/pip3", line 9, in < ...
- tcp/udp只发不接,会丢包还是send失败?
这篇文章源于我看libevent的源码时想到的问题,对于libevent的buffer机制,如果接受端一直不取数据的话,会怎样?如果丢包,不现实,因为会导致数据丢失,如果不丢包,就会导致占用内存一直扩 ...
- js实现默认或者触发一个事件选中元素内容的方法
方法一:非文本框.文本域的选中内容方法 <!Doctype html> <html> <head> <script type="text/javas ...
- LeetCode: Valid Palindrome 解题报告
Valid Palindrome Given a string, determine if it is a palindrome, considering only alphanumeric char ...
- 【Linux】linux下gzip的压缩/解压缩详解
Linux压缩保留源文件的方法: gzip –c filename > filename.gz Linux解压缩保留源文件的方法: gunzip –c filename.gz > file ...
- AIX参数调整
AIX下修改用户最大进程数限制: 用命令查看用户进程数 ps -ef | grep 用户名|wc -l 发现用户进程数达到最大 128 查看用户最大进程数设置 命令 #lsattr -E -l sys ...
- maven项目强制自动更新所有jar包
选中即可:
- Linux下 jenkins 的 使用
在上一张jenkins已经安装成功了,现在开始使用 需要先安装几个插件 选择maven项目 这里会出现错误,点进去去配置 配置JDK,这里用JDK1.8 ,不然jenkins会报错的 这里写上mave ...