国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

live555學習筆記4-計劃任務(TaskScheduler)深入探討

2019-11-08 18:28:27
字體:
來源:轉載
供稿:網友
四 計劃任務(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 copyEventTriggerId BasicTaskScheduler0::createEventTrigger( TaskFunc* eventHandlerProc)  {  unsigned i = fLastUsedTriggerNum;  EventTriggerId mask = fLastUsedTriggerMask;    //在數組中尋找一個未使用的項,把eventHandlerProc分配到這一項。  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參數。這個參數在觸發事件時傳入,見以下函數:[cpp] view plain copyvoid BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId,void* clientData)   {  // First, record the "clientData":  if (eventTriggerId == fLastUsedTriggerMask) {   // common-case optimization:直接保存下clientData  fTriggeredEventClientDatas[fLastUsedTriggerNum] = clientData;  } else {  //從頭到尾查找eventTriggerId對應的項,保存下clientData  EventTriggerId mask = 0x80000000;  for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {  if ((eventTriggerId & mask) != 0) {  fTriggeredEventClientDatas[i] = clientData;    fLastUsedTriggerMask = mask;  fLastUsedTriggerNum = i;  }  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以bit mask的方式記錄需要響應的事件handler們。  fTriggersAwaitingHandling |= eventTriggerId;  }  看,clientData被傳入了,這表明clientData在每次觸發事件時是可以變的。此時再回去看SingleStep()是不是更明了了?

delay task添加時,需要傳入task延遲等待的微秒(百萬分之一秒)數(第一個參數),這個弱智也可以理解吧?嘿嘿。分析一下介個函數:

[cpp] view plain copyTaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,TaskFunc* proc, void* clientData)   {  if (microseconds < 0)  microseconds = 0;  //DelayInterval 是表示時間差的結構  DelayInterval timeToDelay((long) (microseconds / 1000000),(long) (microseconds % 1000000));  //創建delayQueue中的一項  AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData,timeToDelay);  //加入DelayQueue  fDelayQueue.addEntry(alarmHandler);  //返回delay task的唯一標志  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)這個函數是怎么執行的。[cpp] view plain copyvoid 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循環中為什么沒有判斷是否到達最后一下的代碼呢?難道肯定能找到大于新項的等待時間的項嗎?是的!第一個加入項的等待時間是無窮大的,而且這一項永遠存在于隊列中。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 芦山县| 百色市| 宝应县| 静安区| 庆云县| 汾阳市| 营口市| 遵化市| 资兴市| 抚顺市| 丰原市| 新建县| 都安| 桐乡市| 平潭县| 漳平市| 北川| 栖霞市| 嘉定区| 湛江市| 明光市| 山西省| 新宁县| 宜兰县| 图们市| 汨罗市| 新野县| 昆山市| 托克托县| 剑河县| 浏阳市| 永福县| 驻马店市| 茌平县| 新绛县| 新邵县| 禄劝| 汶上县| 普兰县| 西丰县| 宁强县|