1 頁 (共 1 頁)

關於多緒 [about Multi Thread]

發表於 : 2014-09-23, 12:15
admin
最近開始使用 Harbour 3.0.0, 嘗試測試多緒程式問題,摸了半天,把心得
給 post 出來,其實也很簡單,用到兩三個函數而以!

hb_threadStart(): 執行多緒程式...
hb_hb_threadWait(): 偵測多緒程式是否完成.

就這兩個而以,另外有一個 hb_threadJoin(), 測試結果,似乎不太適合,
不適合原因,後面再述~

首先,談談函數語法:

代碼: 選擇全部

   hb_threadStart( <@sStart()> | <bStart> [, <params,...> ] ) 
-> <pThID> 
第一個參數,可以使用函數方式<@sStart>,例如: @test(),
或是使用 codeBlock 方式<bStart>, 例如: {|| test() },
第二個以後為傳遞參數, 例如,執行 test(x1,x2,x3), 就要寫成 hb_threadStart( @test(), x1, x2, x3 )
但是,沒那麼簡單耶~這是我在測試後才發現到的.
當我呼叫該函數 test() 之後,有些 Public 變數在執行時,竟然出現找不到該變數的錯誤訊息,
幾經翻查資料與範例之後才找到原因,那就是修改 hb_threadStart() 第一項參數,

HB_THREAD_INHERIT_PUBLIC
HB_THREAD_INHERIT_PRIVATE
HB_THREAD_INHERIT_MEMVARS
HB_THREAD_MEMVARS_COPY

這四項參數關係到 Public, Private, Memvar 變數宣告傳遞問題,
我的系統中使用 mySQL,所以全部只使用一條 connect 去做資料庫連線,
因此會在主程式宣告 oSQL 為 Public 變數,這樣子全部系統才能直接使用該變數,
所以,這個函數呼叫便要修改為:
hb_threadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, ;
HB_THREAD_MEMVARS_COPY ), ;
{|| ::GetData(@lOK)} ))
這個 lOK 使用 @lOK 方式接收回傳值,藉以判斷 ::GetData() 是否正確讀取資料.
上面提到有一個函數 hb_threadJoin(), 這個變數是用來接收該緒的回傳值,
經測試,每呼叫一次 hb_threadJoin, 就會等待,等這個緒執行完才會繼續下一步!
這樣子,就不符合我們要的多緒功能了,因此便放棄了使用這個 hb_threadJoin 功能,
而直接下參數 @lOK 方式接收回傳值!另外改用 hb_threadWait() 判斷執行緒是否完成.

另外一個函數 hb_threadWait():

代碼: 選擇全部

      hb_threadWait( <pThID> | <apThID>, [ <nTimeOut> ] [, <lAll> ] ) 
               => <nThInd> | <nThCount> | 0 
第一個參數可以為單一序的 ID, 或是存放多緒的陣列<pThID|apThID>,
第二個參數為等待偵測時間<nTimeOut>,
第三個參數為單續或多緒,若傳遞單一個,則為 .f., 若等待多緒完成,則為 .t.

由第二個函數來看,若有多緒程式,那我們就用陣列方式儲存多緒 ID,
例: aadd( aThreads, hb_threadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, ;
HB_THREAD_MEMVARS_COPY ), ;
{|| ::GetData(@lOK)} ))
每次都將單緒存入陣列 aThreads 去,然後再呼叫:

代碼: 選擇全部

           If hb_threadWait( aThreads, 0.1, .t. ) == Len( aThreads ) 
              // 完成 
              Exit 
           EndIf 
藉以判斷多緒<aThreads>是否皆已完成!!

下列就將部分程式碼 post 出來參考!

代碼: 選擇全部

Local aThreads := {} 
           aadd( aThreads, hb_threadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, ; 
                                                     HB_THREAD_MEMVARS_COPY ), ; 
                                           {|| ::GetData(@lOK)} )) 
           MsgThread( aThreads ) 
           If lOK 
              MsgMeter( {|oM, oT, oD, lE| ::OutToExcel(oM, oT, oD, @lE) }, "執行進度:", "輸出 Excel 中......." ) 
           EndIf 
上面功能為呼叫 ::GetData() 函數讀取資料,在讀取過程中是否發生錯誤,藉由 @lOK 變數取得,
如果正常讀取資料,則 lOK := .T., 再將資料輸出至 Excel 去: ::OutToExcel()...

另外 MsgThread() 函數,是在畫面中顯示進度表,這個進度表會向右跑,跑完再往左縮減,
如此反覆執行這個動作,直到所有的 aThreads 都已完成他的工作後,才會執行 If lOK 以下程式碼!

代碼: 選擇全部

Func    MsgThread( aThreads ) 
Local   nID1    := 0,; 
        nID2    := 0 
        // 
        MsgMeter( {|oM, oT, oD, lEnd| MsgThreadGo(oM, oT, oD, lEnd, aThreads)}, '執行進度:', '執行中...', .T. ) 
        // 
Return  NIL 
// 
Func    MsgThreadGo(oM, oT, oD, lEnd, aThreads) 
Local   nTotal  := 100,; 
        nEvery  := 0,; 
        nPer    := 1 
        // 
Local   nS      := 0,; 
        xRetCode:= .F.,; 
        nID     := 0,; 
        lDirection := .T.       // 方向 
        // 
        oM:nTotal := nTotal 
        oD:UpDate() 
        // 
        nS := Seconds() 
        Do While ! lEnd 
           // 檢查執行緒是否執行完畢? 
           If hb_threadWait( aThreads, 0.1, .t. ) == Len( aThreads ) 
              lEnd := .T. 
              Exit 
           EndIf 
           // 進度表顯示判斷 
           If Seconds() - nS >= 0.1 
              If lDirection 
                 nEvery += 1 
              Else 
                 nEvery -= 1 
              EndIf 
              oM:Set( nEvery ) 
              If lDirection 
                 If nEvery == nTotal 
                    lDirection := .F.   // 到達右邊 100 位置,改為負向 
                 EndIf 
              Else 
                 If nEvery == 0 
                    lDirection := .T.   // 到達左邊 0 位置,改為正向 
                 EndIf 
              EndIf 
              nS := Seconds() 
           EndIf 
           oD:UpDate() 
           // 中斷執行續 
           If lEnd 
              hb_threadQuitRequest( hb_threadId() ) 
           EndIf 
           // 
        EndDo 
        // 
Return  NIL 
其實這個函數還有一個 bug, 還沒研究出來,個人有修改 msgMeter() 函數,
可以在等待過程中隨時可以中斷所有執行緒的執行,但是,發現呼叫 hb_threadQuitRequest()
似乎沒作用,不知道是不是函數用錯方式?

若想執行多個執行緒,只要多加幾個 Aadd( aThreads, hb_threadStart(....)) 就可以了.