關於多緒 [about Multi Thread]
發表於 : 2014-09-23, 12:15
最近開始使用 Harbour 3.0.0, 嘗試測試多緒程式問題,摸了半天,把心得
給 post 出來,其實也很簡單,用到兩三個函數而以!
hb_threadStart(): 執行多緒程式...
hb_hb_threadWait(): 偵測多緒程式是否完成.
就這兩個而以,另外有一個 hb_threadJoin(), 測試結果,似乎不太適合,
不適合原因,後面再述~
首先,談談函數語法:
第一個參數,可以使用函數方式<@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():
第一個參數可以為單一序的 ID, 或是存放多緒的陣列<pThID|apThID>,
第二個參數為等待偵測時間<nTimeOut>,
第三個參數為單續或多緒,若傳遞單一個,則為 .f., 若等待多緒完成,則為 .t.
由第二個函數來看,若有多緒程式,那我們就用陣列方式儲存多緒 ID,
例: aadd( aThreads, hb_threadStart( HB_BITOR( HB_THREAD_INHERIT_PUBLIC, ;
HB_THREAD_MEMVARS_COPY ), ;
{|| ::GetData(@lOK)} ))
每次都將單緒存入陣列 aThreads 去,然後再呼叫:
藉以判斷多緒<aThreads>是否皆已完成!!
下列就將部分程式碼 post 出來參考!
上面功能為呼叫 ::GetData() 函數讀取資料,在讀取過程中是否發生錯誤,藉由 @lOK 變數取得,
如果正常讀取資料,則 lOK := .T., 再將資料輸出至 Excel 去: ::OutToExcel()...
另外 MsgThread() 函數,是在畫面中顯示進度表,這個進度表會向右跑,跑完再往左縮減,
如此反覆執行這個動作,直到所有的 aThreads 都已完成他的工作後,才會執行 If lOK 以下程式碼!
其實這個函數還有一個 bug, 還沒研究出來,個人有修改 msgMeter() 函數,
可以在等待過程中隨時可以中斷所有執行緒的執行,但是,發現呼叫 hb_threadQuitRequest()
似乎沒作用,不知道是不是函數用錯方式?
若想執行多個執行緒,只要多加幾個 Aadd( aThreads, hb_threadStart(....)) 就可以了.
給 post 出來,其實也很簡單,用到兩三個函數而以!
hb_threadStart(): 執行多緒程式...
hb_hb_threadWait(): 偵測多緒程式是否完成.
就這兩個而以,另外有一個 hb_threadJoin(), 測試結果,似乎不太適合,
不適合原因,後面再述~
首先,談談函數語法:
代碼: 選擇全部
hb_threadStart( <@sStart()> | <bStart> [, <params,...> ] )
-> <pThID>
或是使用 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
第二個參數為等待偵測時間<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
下列就將部分程式碼 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
如果正常讀取資料,則 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
可以在等待過程中隨時可以中斷所有執行緒的執行,但是,發現呼叫 hb_threadQuitRequest()
似乎沒作用,不知道是不是函數用錯方式?
若想執行多個執行緒,只要多加幾個 Aadd( aThreads, hb_threadStart(....)) 就可以了.