文章出處

task 的目的

做過界面的編程的同學可能會有這種經歷,界面不響應,那,其實程序總是在后臺運行,但是后臺可能是個for循環,那么界面的點擊等事件都不能執行。

在windows界面編程中利用了事件機制來做,維護了事件的隊列,然后有一個線程不停的取出事件然后回調注冊的事件處理函數,每個事件處理函數必然是短小的,否則會獨占處理器太久,其他操作得不到響應。

在Qt中,為了維持對用戶的響應,使用的是signal函數,signal函數觸發slot的操作,理論上也應該是短小的操作。

在兩種機制中,主線程負責ui方面的響應,要不就是把發生的時間記錄下來,要不就是直接signal信號出去,然后立即返回。總體的規則是負責ui的線程中不能出現長時間占用cpu的操作。

task的出現和以上情況是一致的,在使用post task后,函數立即返回,可以執行其他操作,其他組件也可以繼續相應用戶,所有的task應該是短小的操作,以保證其他的task不會產生太大的延遲。

TinyOS中采用了隊列的機制來循環處理task,用線程來講就好像,由boot觸發的main函數像一個主線程,用來記錄用戶的操作以及各種task,而schedule組件就像是一個負責處理的線程,循環的處理用戶已經post的線程,這樣,用戶的操作,如亮燈、終端等,可以得到及時的響應,外界的信息也不會那么容易的丟失。

task 的機制

在tinyos 1.x中task采用的方式是post task,在2.x中使用BasicTasic接口來進行task的注冊,但完全可以用1.x中的方法。

前面提到過post task只是往task隊列中添加一條task調用記錄,然后由scheduler循環處理,在post task和scheduler真正調用task時,一定會產生延遲,也就是說task的調用是推后的函數調用。

因為在1.x中schedular貌似沒有設置task隊列的長度,那么意味著一個組件可以無限post task,但實際上這種情況會使得一個組件獨占cpu資源。

為了避免這種情況,在2.x中schedular設置了的task的調用數組,數組中的每一項是注冊過的task,當task被調用了,就相應的置位,關于順序問題應該是有做記錄(我不知道,求告訴QAQ),這樣可以防止一個組件post過多相同的task獨占資源。

task 和函數的不同

執行操作不同

最主要的不同在于調用,這也是task存在的意義,就像前面說的,task是為了相應用戶而生,調用 task 時在 post 后立即返回,而函數要執行完后返回。

長相也不同

task的標準形式如下

task void 函數名()
{
    // do task
}

這意味著task沒有返回值,也沒有參數。task的思想是為了執行一段操作,調用task時立即返回,所以沒有返回值是情理之中,但post task卻有返回值,我們等會說。

task 的參數我的想法是,TinyOS 的思想是節點的內存有限,所以盡可能減少臨時變量和動態變量這一類變量,所以可以看到文檔建議一些變量最好直接在組件中進行定義,沒有那么多的內存空間來做函數的棧存儲臨時變量。
基于以上分析,task不帶參數,也不幫你存儲參數,但由于這個性質,task運行時可能會產生問題,我們在后面會說到。

是否允許中斷,應該說是搶占?

可以明確的是,task是允許中斷(我覺的更確切應該說是搶占)的,我想這也是保持用戶響應的一部分。同時,我的想法是普通函數是不允許中斷的,因為task是允許中斷的。。。

async 函數意味著可以異步執行,TinyOS規定 async 函數中不能調用普通函數,也就是同步函數,異步的意思貌似是可以搶占其他函數的執行,或者說是打斷,同時自己也可以被搶占。那么如果想要在 async 函數中執行同步代碼,就要用到task,也是唯一方式。

task 返回失敗的情況

首先post task是有返回值的。
為了防止同一組件不停的post task,Tinyos 2.x中post在有同名task正在隊列并且沒有執行時返回Fail,應該就是檢查task調用數組了,這以為著你開啟一個for循環來post數組是不允許的,如下

for (int i = 0; i < 10; i++)
{
    post process_message();
}

上面這代碼,如果第一個post成功,并且process_message函數執行有一定時間,那么根據TinyOS規定,一個process_message在執行,一個process_message函數在隊列,其他的處理函數就都沒有得到執行,返回的是Fail。在這種情況下,你的想法是處理10條消息,但實際上只處理了2條。

這樣看來,這種情況下你所post task的數目與實際執行的task的數目并不一定相同。

但TinyOS允許task自身調用,如下

task void process_message()
{
    // start processing message
    ...
    // finish processing message
    process_message()
}

通過這種方式,task應該是不會返回Fail的,因為task實在結尾處調用自身,如果設置一個counter的話,應該可以實現上面的for循環調用。

In TinyOS 2.x, a basic post will only fail if and only if the task has already been posted and has not started execution.

task 只關注當前

按照線程的思維方式,線程所使用的變量都是進程的,這意味著如果其他線程修改了進行的變量,當前線程可能并不知情。

task執行任務是推遲的,并且task是允許中斷的,這意味這在使用post task后,組件的值隨時可能發生變化,任何一個中斷都有可能修改數據,你post task時所認為task使用的變量,當scheduler真正調用時,可能已經物是人非。

task執行時永遠使用當前組件內最新(updated)的變量值,這意味著你在post task時最好想清楚當task執行時所采用的組件變量是否已經發生改變,必要的話需要采取一定保護變量的措施,如設置變量標志位等,如使用send時采用send_busy標志應該是常用的方法。

其他

我看到 TaskBasic 接口里好像有優先級的設定,這意味著如果加入優先級, task 的順序可能會發生不可控的變化,使用 task 時需要更為細致。

最后

作者水平有限,準確信息請參考TinyOS官方文檔


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()