2009年11月9日 星期一

EVENT呼叫的注意事項-Yield的使用

在PowerBuilder中事件的呼叫方式分為2種,『Post』與『Trigger』。

利用Trigger呼叫事件其實就像你在呼叫Function一樣,原本的程序會暫停(推入堆疊),然後執行Trigger的EVENT對象,執行完畢後,再回到(推出堆疊)原程序繼續執行,若是有回傳值,則會隨之回傳。

利用Post呼叫事件,則是將要呼叫的EVENT對象放到該物件的Message Queue(佇列)中等待被執行,原本程序不中斷繼續執行,因此如果你的事件有回傳值,會根本都收不到(因為還沒執行)

以上是PB對事件呼叫的方式(對Function 也可使用Post)。

原本,這樣的描述執行方式是沒有問題的,但卻有的很大的盲點,那就就是 yield() 指令。

很多人會在迴圈中,尤其是執行起來很久的迴圈,放入yield 指令來讓視窗畫面不會產生LAG現象。但是如果正在執行迴圈的事件程序(Event)或是函式程序(Function)是屬於常常要被post的對象的話,就會很危險。

何謂危險? 舉例來說某一window裡面包含datawindow(dw_1)開始統計鈕(cb_check)裡面有post event ue_check()、停止統計鈕(cb_stop)、一個事件 ue_check 裡面有執行dw_1數萬筆的資料統計,統計完後會修改原始dw_1的來源資料時(也可能新增或是減少 row)
通常ue_test裡面會有的迴圈來負責處理,當處理這樣多的資料時,通常使用者的window畫面會LAG很嚴重,或是"無回應"的現象,但是因為要回應cb_stop的觸發,因此會在迴圈中加入yield(),來讓cb_stop可以讓使用者按下。

首先使用者先按下cb_check,此時會第一次post event ue_check()。
然後使用者不小心再按到了cb_check,正常來說post會放在Queue裡等待不會影響現在正在執行的ue_check,但是由於迴圈中的 yield 會在Queue裡面的等待執行事件或函式優先執行。
此時正在執行迴圈的ue_check事件被中斷了,並被強迫推入堆疊,然後執行了第二次的ue_check事件,若是第二次執行的事件沒有中斷的情況下執行完畢,原本第一次被推入堆疊的ue_check事件就會被推出並繼續執行。

要注意!如果第二次ue_check有改變了dw_1裡面的原始值,或是row的筆數時,將可能造成第一次ue_check回來執行時的無預警錯誤(原始值計算錯誤、筆數提列錯誤等等),而發生程式意外停止。同樣的情況也可能發生在globle變數、Instant變數上。

因此,若要處理yield()指令,一定要相當小心,盡量不要用在會被重複POST或是TIGGER的事件裡面,若一定要使用,可以利用instant變數來記錄該事件是否已經被執行,若是被執行可以以排除第二次執行的可能。

例如:在instant變數加入Boolean ib_incheck = false
然後在ue_check最開頭寫入

if ib_incheck = true then return
ib_incheck = true


在ue_check最後寫入

ib_incheck = false


這樣就可以防止重複被執行的問題。

4 個回應:

阿釗 提到...

很久沒看到這樣深入的PowerBuilder說明,PowerBuilder也很難找到同好討論, 在此給予大力的支持, 好文章

WILDOX 提到...

to 阿釗 :
謝啦!因為太少人討論就只好把自己的心得PO出來分享給需要的網友。

瑾姿 提到...

PowerBuilder同好很少,感謝版主心得分享
藍貓

WILDOX 提到...

TO 瑾姿:
有心得也可以分享給我喔,感恩