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

來源:互聯網
上載者:User
四 計劃任務(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項,其它數量不被支援。以下是函數體

EventTriggerId BasicTaskScheduler0::createEventTrigger( TaskFunc* eventHandlerProc)<br />{<br />unsigned i = fLastUsedTriggerNum;<br />EventTriggerId mask = fLastUsedTriggerMask;</p><p>//在數組中尋找一個未使用的項,把eventHandlerProc分配到這一項。<br />do {<br />i = (i + 1) % MAX_NUM_EVENT_TRIGGERS;<br />mask >>= 1;<br />if (mask == 0)<br />mask = 0x80000000;</p><p>if (fTriggeredEventHandlers[i] == NULL) {<br />// This trigger number is free; use it:<br />fTriggeredEventHandlers[i] = eventHandlerProc;<br />fTriggeredEventClientDatas[i] = NULL; // sanity</p><p>fLastUsedTriggerMask = mask;<br />fLastUsedTriggerNum = i;</p><p>return mask; //分配成功,傳回值表面了第幾項<br />}<br />} while (i != fLastUsedTriggerNum);//表明在數組中迴圈一圈</p><p>//數組中的所有項都被佔用,返回表明失敗。<br />// All available event triggers are allocated; return 0 instead:<br />return 0;<br />}
可以看到最多添加32個事件,且添加事件時沒有傳入clientData參數。這個參數在觸發事件時傳入,見以下函數:
void BasicTaskScheduler0::triggerEvent(EventTriggerId eventTriggerId,void* clientData)<br />{<br />// First, record the "clientData":<br />if (eventTriggerId == fLastUsedTriggerMask) {<br />// common-case optimization:直接儲存下clientData<br />fTriggeredEventClientDatas[fLastUsedTriggerNum] = clientData;<br />} else {<br />//從頭到尾尋找eventTriggerId對應的項,儲存下clientData<br />EventTriggerId mask = 0x80000000;<br />for (unsigned i = 0; i < MAX_NUM_EVENT_TRIGGERS; ++i) {<br />if ((eventTriggerId & mask) != 0) {<br />fTriggeredEventClientDatas[i] = clientData;</p><p>fLastUsedTriggerMask = mask;<br />fLastUsedTriggerNum = i;<br />}<br />mask >>= 1;<br />}<br />}</p><p>// Then, note this event as being ready to be handled.<br />// (Note that because this function (unlike others in the library)<br />// can be called from an external thread, we do this last, to<br />// reduce the risk of a race condition.)<br />//利用fTriggersAwaitingHandling以bit mask的方式記錄需要響應的事件handler們。<br />fTriggersAwaitingHandling |= eventTriggerId;<br />}看,clientData被傳入了,這表明clientData在每次觸發事件時是可以變的。

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

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

TaskToken BasicTaskScheduler0::scheduleDelayedTask(int64_t microseconds,TaskFunc* proc, void* clientData)<br />{<br />if (microseconds < 0)<br />microseconds = 0;<br />//DelayInterval 是表示時間差的結構<br />DelayInterval timeToDelay((long) (microseconds / 1000000),(long) (microseconds % 1000000));<br />//建立delayQueue中的一項<br />AlarmHandler* alarmHandler = new AlarmHandler(proc, clientData,timeToDelay);<br />//加入DelayQueue<br />fDelayQueue.addEntry(alarmHandler);<br />//返回delay task的唯一標誌<br />return (void*) (alarmHandler->token());<br />}</p><p>delay task的執行都在函數fDelayQueue.handleAlarm()中,handleAlarm()在類DelayQueue中實現。看一下handleAlarm():<br />void DelayQueue::handleAlarm()<br />{<br />//如果第一個任務的執行時間未到,則同步一下(重新計算各任務的等待時間)。<br />if (head()->fDeltaTimeRemaining != DELAY_ZERO)<br />synchronize();<br />//如果第一個任務的執行時間到了,則執行第一個,並把它從隊列中刪掉。<br />if (head()->fDeltaTimeRemaining == DELAY_ZERO) {<br />// This event is due to be handled:<br />DelayQueueEntry* toRemove = head();<br />removeEntry(toRemove); // do this first, in case handler accesses queue<br />//執行任務,執行完後會把這一項銷毀。<br />toRemove->handleTimeout();<br />}<br />}可能感覺奇怪,其它的任務隊列都是先搜尋第一個應該執行的項,然後再執行,這裡乾脆,直接執行第一個完事。那就說明第一個就是最應該執行的一個吧?也就是等待時間最短的一個吧?那麼應該在新增工作時,將新任務跟據其等待時間插入到適當的位置而不是追加到尾巴上吧?猜得對不對還得看fDelayQueue.addEntry(alarmHandler)這個函數是怎麼執行的。
void DelayQueue::addEntry(DelayQueueEntry* newEntry)<br />{<br />//重新計算各項的等待時間<br />synchronize();</p><p>//取得第一項<br />DelayQueueEntry* cur = head();<br />//從頭至尾迴圈中將新項與各項的等待時間進行比較<br />while (newEntry->fDeltaTimeRemaining >= cur->fDeltaTimeRemaining) {<br />//如果新項等待時間長於當前項的等待時間,則減掉當前項的等待時間。<br />//也就是後面的等待時幾隻是與前面項等待時間的差,這樣省掉了記錄插入時的時間的變數。<br />newEntry->fDeltaTimeRemaining -= cur->fDeltaTimeRemaining;<br />//下一項<br />cur = cur->fNext;<br />}</p><p>//迴圈完畢,cur就是找到的應插它前面的項,那就插它前面吧<br />cur->fDeltaTimeRemaining -= newEntry->fDeltaTimeRemaining;</p><p>// Add "newEntry" to the queue, just before "cur":<br />newEntry->fNext = cur;<br />newEntry->fPrev = cur->fPrev;<br />cur->fPrev = newEntry->fPrev->fNext = newEntry;<br />}有個問題,while迴圈中為什麼沒有判斷是否到達最後一下的代碼呢?難道肯定能找到大於新項的等待時間的項嗎?是的!第一個加入項的等待時間是無窮大的,而且這一項永遠存在於隊列中。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.