線上訂房服務-台灣趴趴狗聯合訂房中心
發文 回覆 瀏覽次數:2961
推到 Plurk!
推到 Facebook!

關於Sleep對TThread之影響 請各位先進給點建議

答題得分者是:CA
oqqooqqo
一般會員


發表:1
回覆:4
積分:1
註冊:2008-07-03

發送簡訊給我
#1 引用回覆 回覆 發表時間:2011-02-01 12:49:10 IP:60.249.xxx.xxx 訂閱
先說明程式版本為BCB6
最近開始使用thread,發現一些問題特別提出來,如下程式嘗試將旗標變數tt,透過涵數做修改,接著使用sleep進行等待,發現到sleep會使得Execute停止,但若有加入Application->ProcessMessages(),雖然Execute會作動但速度有變,但若是呼叫的函數不會修改到Execute中的變數,則sleep不會影響到thread的進行,詳細請看上傳檔案,檔案原是一位先進分享的,我只是發現這些問題修改來使用。

最後我想請教除了使用Application->ProcessMessages()以外,是否有其他方法可以使sleep不影響thread的運行呢?
[code cpp]
void __fastcall TGridThread1::Execute()
{
SetName();
//---- Place thread code here ----
while(! Terminated )
{
Sleep(5); // Delay program execution for 5 microseconds.
Form1->CGauge1->Progress = ((Form1->CGauge1->Progress 1) % Form1->CGauge1->MaxValue);
if(tt)
{
Form1->Memo1->Lines->Add("Thread 1 Execute in");
tt=false;
}
}
}
[/code]
編輯記錄
oqqooqqo 重新編輯於 2011-01-31 21:51:28, 註解 無‧
taishyang 重新編輯於 2011-02-01 02:12:42, 註解 無‧
DavidLo
高階會員


發表:17
回覆:225
積分:168
註冊:2004-07-21

發送簡訊給我
#2 引用回覆 回覆 發表時間:2011-02-01 23:39:22 IP:125.227.xxx.xxx 訂閱
Thread中的畫面更新,不是應該使用Synchronize(ShowData);
oqqooqqo
一般會員


發表:1
回覆:4
積分:1
註冊:2008-07-03

發送簡訊給我
#3 引用回覆 回覆 發表時間:2011-02-02 09:22:14 IP:123.240.xxx.xxx 訂閱

畫面的更新只是用來展示的,重點是只要透過even修改excute內的變數,使用sleep時,thread也會sleep。

===================引 用 DavidLo 文 章===================
Thread中的畫面更新,不是應該使用Synchronize(ShowData);
編輯記錄
oqqooqqo 重新編輯於 2011-02-01 18:23:19, 註解 無‧
CA
一般會員


發表:1
回覆:10
積分:22
註冊:2007-04-01

發送簡訊給我
#4 引用回覆 回覆 發表時間:2011-02-03 10:02:37 IP:122.116.xxx.xxx 未訂閱
不太懂你在說什麼, 不過稍微看一下程式碼, 覺得你應該是指:
如果 Main Thread 把 tt=true 且該流程內有 Sleep 敘述的話
則你的 Worker Thread 更新 CGuage 的流暢度會受影響

這個問題主要是視窗的執行緒親和性 (Thread Affinity) 問題:
Windows 下面的 UI 元件更新什麼的, 都是由發送訊息驅動的
而訊息是發送給產生該視窗的執行緒, 再由該執行緒去訊息佇列中取出

所以 CGuage 是由主執行緒創建的, 你修改 Progress 會發訊息給它
這是我猜測的, 但如果它只是簡單修改一個成員變數的話, 那可以直接忽略我這篇
發送訊息有兩種: 同步 (SendMessage) 非同步 (PostMessage)
當它用的是同步方式的話, 執行緒會停在 CGuage 修改那一行
等主執行緒處理完訊息後, 才又繼續執行下去
所以主執行緒若處於 Sleep 狀態自然無法處理訊息
大家就在那裏互等

大致上就是這樣一回是



DavidLo
高階會員


發表:17
回覆:225
積分:168
註冊:2004-07-21

發送簡訊給我
#5 引用回覆 回覆 發表時間:2011-02-04 08:36:50 IP:122.126.xxx.xxx 訂閱
void __fastcall TForm1::Button1Click(TObject *Sender)
{
GridThread1->ttest();
Application->ProcessMessages(); //使用上要小心
Sleep(5000); //這5秒內,windows所有程序會停止! 中斷程序除外!
GridThread1->ttest2();
}

編輯記錄
DavidLo 重新編輯於 2011-02-03 17:50:55, 註解 無‧
oqqooqqo
一般會員


發表:1
回覆:4
積分:1
註冊:2008-07-03

發送簡訊給我
#6 引用回覆 回覆 發表時間:2011-02-04 15:36:26 IP:123.240.xxx.xxx 訂閱
先感謝先進的回覆,會寫出這樣的程式主要是因為使用Thread去做CCD的連續取像,若直接使thread->suspend()則Thread會停在excute中任一行,若再使用thread->Resume()有時候會產生無法預期的錯誤,因此為了不讓這問題發生,特別使用tt搭配sleep去確保thread->suspend()是停止於excute迴圈完成後,當thread->Resume()時重新執行excute,防止錯誤發生。

===================引 用 CA 文 章===================
不太懂你在說什麼, 不過稍微看一下程式碼, 覺得你應該是指:
如果 Main Thread 把 tt=true 且該流程內有 Sleep 敘述的話
則你的 Worker Thread 更新 CGuage 的流暢度會受影響

這個問題主要是視窗的執行緒親和性 (Thread Affinity) 問題:
Windows 下面的 UI 元件更新什麼的, 都是由發送訊息驅動的
而訊息是發送給產生該視窗的執行緒, 再由該執行緒去訊息佇列中取出

所以 CGuage 是由主執行緒創建的, 你修改 Progress 會發訊息給它
這是我猜測的, 但如果它只是簡單修改一個成員變數的話, 那可以直接忽略我這篇
發送訊息有兩種: 同步 (SendMessage) 非同步 (PostMessage)
當它用的是同步方式的話, 執行緒會停在 CGuage 修改那一行
等主執行緒處理完訊息後, 才又繼續執行下去
所以主執行緒若處於 Sleep 狀態自然無法處理訊息
大家就在那裏互等

大致上就是這樣一回是



CA
一般會員


發表:1
回覆:10
積分:22
註冊:2007-04-01

發送簡訊給我
#7 引用回覆 回覆 發表時間:2011-02-04 17:13:42 IP:122.116.xxx.xxx 未訂閱
所謂產生無法預期的錯誤是指什麼錯誤?

在某個執行緒去 Suspend 另一個執行緒是很危險的事, 照理說應該避免
因為你不知道另一個執行緒目前執行到哪裡! 例如: 它可能正在 new 一塊
記憶體, 而這在 Windows 中會去 Heap 拿記憶體, 因為這是共用的資源
系統會在分配前先鎖定 (就像進入臨界區間一樣) , 如果這時候它被別的
執行緒 Suspend, 而呼叫 Suspend 的執行緒在後面的程式中也需要分
配動態記憶體, 這時候就會造成死結. 比較安全的狀況只有執行緒自己
呼叫 Suspend, 而由別人呼叫 Resume, 但即使是這樣也要很小心, 才
不會有意外

至於用 Sleep 要精確控制時間, 是不太正確的說法, Sleep 本身的精度
也都才只有 16ms 而已, 要如何確保它是在 execute 迴圈完成後停止?

其實不論怎麼說, 只要你要更新 UI, 那麼產生那個 UI 的執行緒一定要處
於空閒的狀態, 如果他在忙碌, 就沒辦法更新該 UI, 那麼更新的動作就會
被延遲, 你的主執行緒去 Sleep 就會造成它在 Sleep 期間無法更新, 其他
人就得等待, Sleep 多久就等多久, 用 ApplicationProcess busy loop
是因為你主動去取訊息, 所以才不會有延遲的感覺, 但是當然, 如果 busy loop
裡面有 Sleep 的話, 它的更新頻率就會受影響


===================引 用 oqqooqqo 文 章===================
先感謝先進的回覆,會寫出這樣的程式主要是因為使用Thread去做CCD的連續取像,若直接使thread->suspend()則Thread會停在excute中任一行,若再使用thread->Resume()有時候會產生無法預期的錯誤,因此為了不讓這問題發生,特別使用tt搭配sleep去確保thread->suspend()是停止於excute迴圈完成後,當thread->Resume()時重新執行excute,防止錯誤發生。
GrandRURU
站務副站長


發表:234
回覆:1651
積分:1742
註冊:2005-06-21

發送簡訊給我
#8 引用回覆 回覆 發表時間:2011-02-05 21:17:03 IP:115.43.xxx.xxx 未訂閱

編輯記錄
GrandRURU 重新編輯於 2011-02-06 05:49:18, 註解 無‧
oqqooqqo
一般會員


發表:1
回覆:4
積分:1
註冊:2008-07-03

發送簡訊給我
#9 引用回覆 回覆 發表時間:2011-02-05 22:48:29 IP:123.240.xxx.xxx 訂閱
正如您所說,錯誤是發生在記憶體配置時,原始運作機制如下,一直查訪bool StopFlag並且透過sleep等待thread停止,而這樣的做法就會發生一開始討論的問題。


[code cpp]
void __fastcall TGridThread1::Execute()
{
SetName();
//---- Place thread code here ----
while(! Terminated )
{
Sleep(5); // Delay program execution for 5 microseconds.
Form1->CGauge1->Progress = ((Form1->CGauge1->Progress 1) % Form1->CGauge1->MaxValue);
StopFlag=false; // new add
if(tt)
{
Form1->Memo1->Lines->Add("Thread 1 Execute in");
StopFlag=true; //new add
tt=false;
Suspend(); //new add
}
}
}
[/code]


===================引 用 CA 文 章===================
所謂產生無法預期的錯誤是指什麼錯誤?

在某個執行緒去 Suspend 另一個執行緒是很危險的事, 照理說應該避免
因為你不知道另一個執行緒目前執行到哪裡! 例如: 它可能正在 new 一塊
記憶體, 而這在 Windows中會去 Heap 拿記憶體, 因為這是共用的資源
系統會在分配前先鎖定 (就像進入臨界區間一樣) , 如果這時候它被別的
執行緒 Suspend, 而呼叫 Suspend 的執行緒在後面的程式中也需要分
配動態記憶體, 這時候就會造成死結. 比較安全的狀況只有執行緒自己
呼叫 Suspend, 而由別人呼叫 Resume, 但即使是這樣也要很小心, 才
不會有意外

至於用 Sleep 要精確控制時間, 是不太正確的說法, Sleep 本身的精度
也都才只有 16ms 而已, 要如何確保它是在 execute 迴圈完成後停止?

其實不論怎麼說, 只要你要更新 UI, 那麼產生那個 UI 的執行緒一定要處
於空閒的狀態, 如果他在忙碌, 就沒辦法更新該 UI, 那麼更新的動作就會
被延遲, 你的主執行緒去 Sleep 就會造成它在 Sleep 期間無法更新, 其他
人就得等待, Sleep 多久就等多久, 用 ApplicationProcess busy loop
是因為你主動去取訊息, 所以才不會有延遲的感覺, 但是當然, 如果 busy loop
裡面有 Sleep 的話, 它的更新頻率就會受影響


編輯記錄
oqqooqqo 重新編輯於 2011-02-05 07:51:17, 註解 無‧
oqqooqqo 重新編輯於 2011-02-05 07:52:24, 註解 無‧
CA
一般會員


發表:1
回覆:10
積分:22
註冊:2007-04-01

發送簡訊給我
#10 引用回覆 回覆 發表時間:2011-02-06 00:39:45 IP:122.116.xxx.xxx 未訂閱
首先在 TThread 裡面修改到 VCL 元件的時候, 應該用 Synchronize 包起來
這個函式其實就是去送個訊息給主執行緒 (透過 SendMessage 這個 API)
這是一個同步呼叫, 像前面說的, 執行緒會等待主執行緒執行完才繼續, 所以
主執行緒絕對要處於空閒狀態, 否則它 Sleep 多久, TThread 就要等多久

話說回來, 為什麼要包起來我並不是很清楚, 如果沒包起來又會怎樣? 不知道
但我想免不了, 原始碼中對 TMemo / CGuage 的修改多少也會發送同步送訊息
所以一樣會有等待主執行緒的情形, 這很正常, Windows 元件就是這樣設計的
如果原始碼中沒有發送訊息給主執行緒, 你又沒有用 Synchronize 包起來, 會發
生什麼非預期的事我就不知道了, 為什麼會發生我也不得而知


回說你的程式, 小弟沒有 BCB6 因此沒去執行程式, 不是很確定問題是怎樣, 你說:
"若是呼叫的函數不會修改到Execute中的變數,則sleep不會影響到thread的進行"
照理說應該不會這樣, 修改 tt 只是一個變數而已, 對執行緒的流暢度應該沒有影響
反而是你的 Button1Click, Button2Click, Button3Click 內容就不太一樣了, 其中的
Button1Click 裡面用迴圈加 Application->ProcessMessages 但是每次只睡 10 ms
Button2Click 跟 Button3Click 卻睡了 5000 ms, 依照前面講的, 修改 VCL 元件會
等待主執行緒空閒, 這三個按鈕的睡眠時間不同, 影響當然不同, 睡 10 ms 的會比較流暢

回到你上面這一段, 既然有 StopFlag 可以讓主執行緒去偵測, 且後面有呼叫 Suspend()
主執行緒不用 Sleep 應該是沒差吧! 不過如果只是要等待 TGridThread 處於懸掛的狀態
不需要這個 StopFlag, 因為 TThread 有一個 Suspended 的屬性, 可用來偵測是否懸掛

但這只是暫停時機的問題. 如果你的問題是主執行緒 Sleep 的時候發現 TGridThread 也停
下來, 問題就是上面說的, 它會等待主執行緒, 你可以不等待, 但是仍然不能用 TGridThread
修改 VCL 元件, 你必須送訊息給主執行緒去修改, 我看 BCB5 只有 Synchronize 是同步呼叫
不知道後面版本有沒有新增非同步的版本, 另外 BCB5 有一個 Perform 的函式可以模擬發送
訊息, 但這個東西是用呼叫它的執行緒去執行的, 並不能達到目的.

非同步的版本在 Win32 就是用 PostMessage API 來送訊息
如果在 Java 可以用 invokeLater, 在 C# 可以用 BeginInvoke, EndInvoke 來達成
如果你有學過這兩種, 大概就是那種意思





編輯記錄
CA 重新編輯於 2011-02-05 09:45:12, 註解 無‧
CA 重新編輯於 2011-02-05 09:48:19, 註解 無‧
oqqooqqo
一般會員


發表:1
回覆:4
積分:1
註冊:2008-07-03

發送簡訊給我
#11 引用回覆 回覆 發表時間:2011-02-06 16:28:01 IP:123.240.xxx.xxx 訂閱
感謝先進的回覆,我原本加入sleep只是為了在等待thread Suspend時能將資源給其他thread使用,那現在將會考慮直接使用while直接確認thread是否Suspend。
===================引 用 CA 文 章===================

回到你上面這一段, 既然有 StopFlag 可以讓主執行緒去偵測, 且後面有呼叫 Suspend()
主執行緒不用 Sleep 應該是沒差吧! 不過如果只是要等待 TGridThread 處於懸掛的狀態
不需要這個 StopFlag, 因為 TThread 有一個 Suspended 的屬性, 可用來偵測是否懸掛



系統時間:2017-10-17 17:37:01
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!