live555学习笔记4-计划任务(TaskScheduler)深入探讨

来源:互联网 发布:数据流程图例题与答案 编辑:程序博客网 时间:2024/05/16 01:41
四 计划任务(TaskScheduler)深入探讨

我们且把三种任务命名为:socket handler,event handler,delay task

这三种任务的特点是,前两个加入执行队列后会一直存在,而delay task在执行完一次后会立即弃掉。

socket handler保存在队列BasicTaskScheduler0::HandlerSet* fHandlers中;

event handler保存在数组BasicTaskScheduler0::TaskFunc * fTriggeredEventHandlers[MAX_NUM_EVENT_TRIGGERS]中;

delay task保存在队列BasicTaskScheduler0::DelayQueue fDelayQueue中。


下面看一下三种任务的执行函数的定义:
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一样。

再看一下向任务调度对象添加三种任务的函数的样子:
delay task为:
void setBackgroundHandling(int socketNum, int conditionSet ,BackgroundHandlerProc* handlerProc, void* clientData)
event handler为:
EventTriggerId createEventTrigger(TaskFunc* eventHandlerProc)
delay task为:
TaskToken scheduleDelayedTask(int64_t microseconds, TaskFunc* proc,void* clientData)

socket handler添加时为什么需要那些参数呢?socketNum是需要的,因为要select socket(socketNum即是socket()返回的那个socket对象)。conditionSet也是需要的,它用于表明socket在select时查看哪种装态,是可读?可写?还是出错?proc和clientData这两个参数就不必说了(真有不明白的吗?)。再看BackgroundHandlerProc的参数,socketNum不必解释,mask是什么呢?它正是对应着conditionSet,但它表明的是select之后的结果,比如一个socket可能需要检查其读/写状态,而当前只能读,不能写,那么mask中就只有表明读的位被设置。

event handler是被存在数组中。数组大小固定,是32项,用EventTriggerId来表示数组中的项,EventTriggerId是一个32位整数,因为数组是32项,所以用EventTriggerId中的第n位置1表明对应数组中的第n项。成员变量fTriggersAwaitingHandling也是EventTriggerId类型,它里面置1的那些位对应了数组中所有需要处理的项。这样做节省了内存和计算,但降低了可读性,呵呵,而且也不够灵活,只能支持32项或64项,其它数量不被支持。以下是函数体

[cpp] view plain copy
  1. EventTriggerId BasicTaskScheduler0::createEventTrigger( TaskFunc* eventHandlerProc)  
  2. {  
  3. unsigned i = fLastUsedTriggerNum;  
  4. EventTriggerId mask = fLastUsedTriggerMask;  
  5.   
  6. //在数组中寻找一个未使用的项,把eventHandlerProc分配到这一项。  
  7. do {  
  8. i = (i + 1) % MAX_NUM_EVENT_TRIGGERS;  
  9. mask >>= 1;  
  10. if (mask == 0)  
  11. mask = 0x80000000;  
  12.   
  13. if (fTriggeredEventHandlers[i] == NULL) {  
  14. // This trigger number is free; use it:  
  15. fTriggeredEventHandlers[i] = eventHandlerProc;  
  16. fTriggeredEventClientDatas[i] = NULL; // sanity  
  17.   
  18. fLastUsedTriggerMask = mask;  
  19. fLastUsedTriggerNum = i;  
  20.   
  21. return mask; //分配成功,返回值表面了第几项  
  22. }  
  23. while (i != fLastUsedTriggerNum);//表明在数组中循环一圈  
  24.   
  25. //数组中的所有项都被占用,返回表明失败。  
  26. // All available event triggers are allocated; return 0 instead:  
  27. return 0;  
  28. }  

可以看到最多添加32个事件,且添加事件时没有传入clientData参数。这个参数在触发事件时传入,见以下函数:
[cpp] view plain copy
  1. void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId,void* clientData)   
  2. {  
  3. // First, record the "clientData":  
  4. if (eventTriggerId == fLastUsedTriggerMask) {   
  5. // common-case optimization:直接保存下clientData  
  6. fTriggeredEventClientDatas[fLastUsedTriggerNum] = clientData;  
  7. else {  
  8. //从头到尾查找eventTriggerId对应的项,保存下clientData  
  9. EventTriggerId mask = 0x80000000;  
  10. for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {  
  11. if ((eventTriggerId & mask) != 0) {  
  12. fTriggeredEventClientDatas[i] = clientData;  
  13.   
  14. fLastUsedTriggerMask = mask;  
  15. fLastUsedTriggerNum = i;  
  16. }  
  17. mask >>= 1;  
  18. }  
  19. }  
  20.   
  21. // Then, note this event as being ready to be handled.  
  22. // (Note that because this function (unlike others in the library)   
  23. // can be called from an external thread, we do this last, to  
  24. // reduce the risk of a race condition.)  
  25. //利用fTriggersAwaitingHandling以bit mask的方式记录需要响应的事件handler们。  
  26. fTriggersAwaitingHandling |= eventTriggerId;  
  27. }  
看,clientData被传入了,这表明clientData在每次触发事件时是可以变的。

此时再回去看SingleStep()是不是更明了了?

delay task添加时,需要传入task延迟等待的微秒(百万分之一秒)数(第一个参数),这个弱智也可以理解吧?嘿嘿。分析一下介个函数:

[cpp] view plain copy
  1. TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,TaskFunc* proc, void* clientData)   
  2. {  
  3. if (microseconds < 0)  
  4. microseconds = 0;  
  5. //DelayInterval 是表示时间差的结构  
  6. DelayInterval timeToDelay((long) (microseconds / 1000000),(long) (microseconds % 1000000));  
  7. //创建delayQueue中的一项  
  8. AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData,timeToDelay);  
  9. //加入DelayQueue  
  10. fDelayQueue.addEntry(alarmHandler);  
  11. //返回delay task的唯一标志  
  12. return (void*) (alarmHandler->token());  
  13. }  
  14.   
  15. delay task的执行都在函数fDelayQueue.handleAlarm()中,handleAlarm()在类DelayQueue中实现。看一下handleAlarm():  
  16. void DelayQueue::handleAlarm()   
  17. {  
  18. //如果第一个任务的执行时间未到,则同步一下(重新计算各任务的等待时间)。  
  19. if (head()->fDeltaTimeRemaining != DELAY_ZERO)  
  20. synchronize();  
  21. //如果第一个任务的执行时间到了,则执行第一个,并把它从队列中删掉。  
  22. if (head()->fDeltaTimeRemaining == DELAY_ZERO) {  
  23. // This event is due to be handled:  
  24. DelayQueueEntry* toRemove = head();  
  25. removeEntry(toRemove); // do this first, in case handler accesses queue  
  26. //执行任务,执行完后会把这一项销毁。  
  27. toRemove->handleTimeout();  
  28. }  
  29. }  
可能感觉奇怪,其它的任务队列都是先搜索第一个应该执行的项,然后再执行,这里干脆,直接执行第一个完事。那就说明第一个就是最应该执行的一个吧?也就是等待时间最短的一个吧?那么应该在添加任务时,将新任务跟据其等待时间插入到适当的位置而不是追加到尾巴上吧?猜得对不对还得看fDelayQueue.addEntry(alarmHandler)这个函数是怎么执行的。
[cpp] view plain copy
  1. void DelayQueue::addEntry(DelayQueueEntry* newEntry)   
  2. {  
  3. //重新计算各项的等待时间  
  4. synchronize();  
  5.   
  6. //取得第一项  
  7. DelayQueueEntry* cur = head();  
  8. //从头至尾循环中将新项与各项的等待时间进行比较  
  9. while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) {  
  10. //如果新项等待时间长于当前项的等待时间,则减掉当前项的等待时间。  
  11. //也就是后面的等待时几只是与前面项等待时间的差,这样省掉了记录插入时的时间的变量。  
  12. newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;  
  13. //下一项  
  14. cur = cur->fNext;  
  15. }  
  16.   
  17. //循环完毕,cur就是找到的应插它前面的项,那就插它前面吧  
  18. cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;  
  19.   
  20. // Add "newEntry" to the queue, just before "cur":  
  21. newEntry->fNext = cur;  
  22. newEntry->fPrev = cur->fPrev;  
  23. cur->fPrev = newEntry->fPrev->fNext = newEntry;  
  24. }  
有个问题,while循环中为什么没有判断是否到达最后一下的代码呢?难道肯定能找到大于新项的等待时间的项吗?是的!第一个加入项的等待时间是无穷大的,而且这一项永远存在于队列中。
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 车的行驶本丢了怎么办 车和行驶证丢了怎么办 考驾照人在外地怎么办 外地考驾照没有居住证怎么办 考驾驶证预约密码忘了怎么办 考驾照密码忘了怎么办 考驾照的密码忘了怎么办 手机银行登录密码忘了怎么办 宽带账号或密码错误怎么办 车险过户联系不上原车主怎么办 换车了etc忘拆了怎么办 c1d驾驶证d证到期了怎么办 摩托车驾驶证过五年怎么办 没居住证想上东莞牌怎么办 外地考驾照需要暂住证怎么办 考驾照期间暂住证过期怎么办 b2驾照扣了6分怎么办 c1驾照扣了11分怎么办 c1驾驶证分扣9分怎么办 驾驶证c照扣6分怎么办 驾照过期1个月怎么办 上海驾驶证b证扣分怎么办 临时牌驾照丢了怎么办 行驶证年审过期两年怎么办 驾证到期了没换怎么办 在非洲被蚊子咬怎么办 身份证丢了被非法贷款怎么办 未满16岁怎么办身份证 放弃继承权后想反悔怎么办 上海居住证积分中社保断怎么办 换驾驶证但是身份证地址变动怎么办 驾驶证b证扣分了怎么办 c1驾证过期没审怎么办 驾照报名三年过期了怎么办 新车行驶证过期了怎么办 行驶证忘了审怎么办 摩托车驾驶证副本丢了怎么办 人被全险车撞了怎么办 被全险车撞了怎么办 驾驶证和行驶证遗失怎么办 户口迁移身份证没换驾照怎么办