live555峰哥的私房菜(二)-----计划任务(TaskScheduler)探讨
计划任务(TaskScheduler)探讨
socket handler 为
typedef void BackgroundHandlerProc (void* clientData, int mask);
event handler为
typedef void TaskFunc(void* clientData);
delay task 为
typedef void TaskFunc(void* clientData);// 跟event handler一样。
再看一下向任务调度对象添加三种任务的函数的样子:
socket handler 为:
void setBackgroundHandling(int socketNum, int conditionSet ,BackgroundHandlerProc*
handlerProc, void* clientData)
EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc)
delay task 为:
TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc,void*
clientData)
(socketNum 即是socket() 返回的那个socket 对象)。conditionSet 也是需要的,它用于
表明socket 在select 时查看哪种装态,是可读?可写?还是出错?proc 和clientData 这两
个参数就不必说了(真有不明白的吗?)。再看BackgroundHandlerProc 的参数,socketNum
不必解释,mask是什么呢?它正是对应着 conditionSet ,但它表明的是 select 之后的结果,
比如一个socket 可能需要检查其读/ 写状态,而当前只能读,不能写,那么mask中就只有
表明读的位被设置。
- void BasicTaskScheduler
- ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
- if (socketNum < 0) return;
- FD_CLR((unsigned)socketNum, &fReadSet);
- FD_CLR((unsigned)socketNum, &fWriteSet);
- FD_CLR((unsigned)socketNum, &fExceptionSet);
- if (conditionSet == 0) {
- fHandlers->clearHandler(socketNum);
- if (socketNum+1 == fMaxNumSockets) {
- --fMaxNumSockets;
- }
- } else {
- fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
- if (socketNum+1 > fMaxNumSockets) {
- fMaxNumSockets = socketNum+1;
- }
- if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
- if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
- if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);
- }
- }
event handler是被存在数组中。数组大小固定,是32项,用 EventTriggerId 来表示数组中
的项,EventTriggerId 是一个32位整数,因为数组是32项,所以用EventTriggerId 中的
第n 位置1表明对应数组中的第n 项。成员变量fTriggersAwaitingHandling 也是
EventTriggerId 类型,它里面置 1 的那些位对应了数组中所有需要处理的项。这样做节省了
内存和计算,但降低了可读性,呵呵,而且也不够灵活,只能支持32项或64项,其它数
量不被支持。以下是函数体
- EventTriggerId BasicTaskScheduler0::createEventTrigger(TaskFunc* eventHandlerProc) {
- unsigned i = fLastUsedTriggerNum;
- EventTriggerId mask = fLastUsedTriggerMask;
- do {
- i = (i+1)%MAX_NUM_EVENT_TRIGGERS;
- mask >>= 1;
- if (mask == 0) mask = 0x80000000;
- if (fTriggeredEventHandlers[i] == NULL) {
- // This trigger number is free; use it:
- fTriggeredEventHandlers[i] = eventHandlerProc;
- fTriggeredEventClientDatas[i] = NULL; // sanity
- fLastUsedTriggerMask = mask;
- fLastUsedTriggerNum = i;
- return mask;
- }
- } while (i != fLastUsedTriggerNum);
- // All available event triggers are allocated; return 0 instead:
- return 0;
- }
可以看到最多添加32个事件,且添加事件时没有传入 clientData 参数。这个参数在
触发事件时传入,见以下函数:
- void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId, void* clientData) {
- // First, record the "clientData". (Note that we allow "eventTriggerId" to be a combination of bits for multiple events.)
- EventTriggerId mask = 0x80000000;
- for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {
- if ((eventTriggerId&mask) != 0) {
- fTriggeredEventClientDatas[i] = clientData;
- }
- mask >>= 1;
- }
- // Then, note this event as being ready to be handled.
- // (Note that because this function (unlike others in the library) can be called from an external thread, we do this last, to
- // reduce the risk of a race condition.)
- fTriggersAwaitingHandling |= eventTriggerId;
- }
看,clientData 被传入了,这表明 clientData 在每次触发事件时是可以变的。此时再回去看
SingleStep()是不是更明了了?
弱智也可以理解吧?嘿嘿。分析一下介个函数:
- TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,
- TaskFunc* proc,
- void* clientData) {
- if (microseconds < 0) microseconds = 0;
- DelayInterval timeToDelay((long)(microseconds/1000000), (long)(microseconds%1000000));
- AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData, timeToDelay);
- fDelayQueue.addEntry(alarmHandler);
- return (void*)(alarmHandler->token());
- }
delay task的执行都在函数fDelayQueue.handleAlarm() 中,handleAlarm()在类
DelayQueue 中实现。看一下handleAlarm():
- void DelayQueue::handleAlarm() {
- //如果第一个任务的执行时间未到,则同步一下(重新计算各任务的等待时间)。
- if (head()->fDeltaTimeRemaining != DELAY_ZERO) synchronize();
- //如果第一个任务的执行时间到了,则执行第一个,并把它从队列中删掉。
- if (head()->fDeltaTimeRemaining == DELAY_ZERO) {
- // This event is due to be handled:
- DelayQueueEntry* toRemove = head();
- removeEntry(toRemove); // do this first, in case handler accesses queue
- //执行任务,执行完后会把这一项销毁。
- toRemove->handleTimeout();
- }
- }
直接执行第一个完事。那就说明第一个就是最应该执行的一个吧?也就是等待时间最短的一
个吧?那么应该在添加任务时,将新任务跟据其等待时间插入到适当的位置而不是追加到尾
巴上吧?猜得对不对还得看fDelayQueue.addEntry(alarmHandler) 这个函数是怎么执行的。
- void DelayQueue::addEntry(DelayQueueEntry* newEntry) {
- // 重新计算各项的等待时间
- synchronize();
- // 取得第一项
- DelayQueueEntry* cur = head();
- // 从头至尾循环中将新项与各项的等待时间进行比较
- while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) {
- // 如果新项等待时间长于当前项的等待时间,则减掉当前项的等待时间。
- newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;
- cur = cur->fNext;
- }
- //循环完毕,cur 就是找到的应插它前面的项,那就插它前面吧
- cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;
- // Add "newEntry" to the queue, just before "cur":
- newEntry->fNext = cur;
- newEntry->fPrev = cur->fPrev;
- cur->fPrev = newEntry->fPrev->fNext = newEntry;
- }
有个问题,while循环中为什么没有判断是否到达最后一下的代码呢?难道肯定能找到大于
新项的等待时间的项吗?是的!第一个加入项的等待时间是无穷大的,而且这一项永远存在
于队列中。
live555峰哥的私房菜(二)-----计划任务(TaskScheduler)探讨的更多相关文章
- 鸟哥Linux私房菜知识汇总8至9章
一看最近<鸟哥Linux私房菜>. 这是一个基本的书,万丈高楼平地起,学. 这是我整理的一些知识点.尽管非常基础. 希望和大家共同交流. 第8章 Linux磁盘与文件系统管理 一.Linu ...
- C#私房菜[二][提供编程效率的技巧]
AaronYang的C#私房菜[二][提供编程效率的技巧] 前言 我的文章简单易懂,能学到东西.因为复杂的东西,讲起来,好累.阅读者只是膜拜,学不到东西,就是没用的东西,好多文章都是看不下去.我写不出 ...
- 鸟哥Linux私房菜基础学习篇学习笔记3
鸟哥Linux私房菜基础学习篇学习笔记3 第十二章 正则表达式与文件格式化处理: 正则表达式(Regular Expression) 是通过一些特殊字符的排列,用以查找.删除.替换一行或多行文字字符: ...
- [Linux]《鸟哥的私房菜》笔记 (缓慢更新)
暂时不更新了..这几天一看起书来发现内容很多,这样写blog太慢,也没意义.所以现在是每天看书,在笔记本上记笔记,再配合着<操作系统>和 linux内核 加深理解.往后会以心得体会为主写一 ...
- linux: 鸟哥的私房菜
鸟哥的私房菜 http://vbird.dic.ksu.edu.tw/linux_basic/0320bash.php
- 鸟哥Linux私房菜基础学习篇学习笔记2
鸟哥Linux私房菜基础学习篇学习笔记2 第九章 文件与文件系统的压缩打包: Linux下的扩展名没有什么特殊的意义,仅为了方便记忆. 压缩文件的扩展名一般为: *.tar, *.tar.gz, *. ...
- 鸟哥Linux私房菜基础学习篇学习笔记1
鸟哥Linux私房菜基础学习篇学习笔记1 第三章 主导分区(MBR),当系统在开机的时候会主动去读取这个区块的内容,必须对硬盘进行分区,这样硬盘才能被有效地使用. 所谓的分区只是针对64Bytes的分 ...
- 鸟哥linux私房菜第6章笔记
鸟哥linux私房菜第6章笔记 文件权限 修改 chgrp [-R] groupname filename //修改文件所属组 chown [-R] ownername[:groupname] fil ...
- 学习鸟哥linux私房菜--安装centos5.6(u盘安装,中文乱码)
题头为"学习鸟哥Linux私房菜"的内容,均为博主在看鸟哥的Linux私房菜第三版的学习经历收获.以下正文: 鸟哥第一部分讲Linux规则与安装,看到第四章正式开始讲实际安装,于是 ...
随机推荐
- ZOJ4062 Plants vs. Zombies(二分+贪心)
题目链接:传送门 题目大意: 有n棵植物依次放在1-n,机器人从0出发浇水,每棵植物被浇水时di += ai,求浇m次水后min{di|1 ≤ i ≤ n}的最大值.(浇水时必须往左或往右走一步,落脚 ...
- 腾讯云JavaWeb环境配置
腾讯云服务器Centos7系统配置javaWeb运行环境 java1.8 运行命令列表 yum list java-* 安装相应版本的jdk,一般含有devel的就是真正的jdk,如:java-1.8 ...
- 实验吧—密码学——WP之 困在栅栏里的凯撒
首先我们研究题目.栅栏:凯撒 还发现一个数字“6”,那么我们很容易就有一个解题思路 对这段字符进行栅栏解密再进行凯撒解密 我们都知道,栅栏解密的关键就是栏数,根据题目中我们发现的信息,这段字符串是12 ...
- LeetCode - Most Frequent Subtree Sum
Given the root of a tree, you are asked to find the most frequent subtree sum. The subtree sum of a ...
- 【vue】遇到的问题
[一]项目编译的时候报错 npm install npm WARN @mtfe/thrift@2.3.7 requires a peer of thrift@0.11.0 but none is in ...
- day 06云计算的三种服务模式:IaaS,PaaS和SaaS
云计算的三种服务模式:IaaS,PaaS和SaaS ”云服务”现在已经快成了一个家喻户晓的词了.如果你不知道PaaS, IaaS 和SaaS的区别,那么也没啥,因为很多人确实不知道. “云”其实是互联 ...
- 使用sync 修饰符------子组件内部可以修改props
首先看一个需求,外部点击一个按钮,让弹窗组件显示(也就是将弹窗组件显示的flag置为true),点击弹窗组件内部的某个按钮 ,让改props置为false,关闭弹窗,但是会报警告,因为内层组件不能修改 ...
- itcast-Hibernate orm元数据和 关系操作
在Hibernate安装包 project /etc/hibernate.property文件下 显示 ,格式化 映射导入映射文件 详解orm元数据 配置文件详解 generator主键生 ...
- plsql快速入门
汉化plsql方法: 本来想直接使用英文版的,但是太多专业名词看不懂,只好先汉化熟悉一下先. 安装好plsq后,百度下载plsql汉化包 执行汉化包里面的安装程序,安装目标路径选择plsql的安装路径 ...
- terraform 几个方便的工具
几个方便的terraform 工具,方便了解terraform terraform-docs 方便的查看资源的信息(支持markdown,json 格式),对于ci/cd 很方便 项目地址 https ...