全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:2370
推到 Plurk!
推到 Facebook!

Thread 開發上的問題

答題得分者是:pcplayer99
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#1 引用回覆 回覆 發表時間:2018-03-14 14:10:50 IP:118.169.xxx.xxx 未訂閱
請問各位:

firemonkey 的執行緒(Threading) 要如何使用, 我參考網路很多文獻, 至少有3-5種寫法
有的用
TThread.CreateAnonymousThread;
TThread.Synchronize(nil, procedure
begin
.....
end);

也有直接用
TThread.Synchronize(TThread.CurrentThread, 要執行的procedure);
還有以Task 方式撰寫

恕小弟在這方面是第一次接觸, 我參考各方的設計想做出一個同時執行兩組的 procedure 功能, 但就是達不到同時運作
第1組 ActiveDialog 是一個轉圈的計數顯示, 第2組是一個簡單的迴圈(只是做測試用)
我想要在第2組跑迴圈時 for .. next 同時, 第1組計數顯示同時會轉圈, 當 第2組跑完, 計數器也停止
但加入thread 後, 永遠都是第2組跑完, 第1組才會有效果
我相信是我對Thrad不懂原理的原故, 所以想請各位先進指點一二, 謝謝!

這是我其中一種寫法__

procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
FActivityDialogThread: TThread;
begin
// ActiveDialog 是第1組計數器, 我是以 TFrame 下去設置的
ActiveDialog1.timer := 100;
ActiveDialog1.countdown := 5;
FActivityDialogThread := TThread.CreateAnonymousThread(procedure
begin
try
TThread.Synchronize(nil, procedure
var i : integer;
begin
for i := 0 to 100 do
begin
sleep(50);
Label1.Text := i.ToString;
end;
end);
finally
end;
end);
FActivityDialogThread.FreeOnTerminate := False;
FActivityDialogThread.Start;
// Timer1 是放在Form1 上, 每100ms 會執行一次(就是要跑轉圈的特效)
Timer1.Enabled := true;
end;

// 每 100ms 執行一次
procedure TForm1.Timer1Timer(Sender: TObject);
begin
ActiveDialog1.ActiveRun;
end;

// 這是 TFrame 中的g執行碼部份
// 目前是沒有加入 Thread, 但我有試過, 把 ActiveRun 改為上述 Threading做法, 結果是一樣的
......
public
{ Public declarations }
timer : integer; // 指定 ms 間隔
countdown : integer; // 指定要倒數幾次
procedure ActiveRun;
......
procedure TActiveDialog.ActiveRun;
begin
if countdown = 0 then
begin
Circle.Fill.Color := TAlphaColorRec.Red;
Run.Stroke.Color := $FFE0E0E0;
exit;
end;

timer := timer 1;
if timer < 20 then
begin
Run.EndAngle := 360 / (100 / (timer * 5));
end
else
begin
Run.EndAngle := 0;
Run.StartAngle := -90;
if Circle.Fill.Color = $FFE0E0E0 then
begin
Circle.Fill.Color := TAlphaColorRec.Red;
Run.Stroke.Color := $FFE0E0E0;
end
else
begin
Circle.Fill.Color := $FFE0E0E0;
Run.Stroke.Color := TAlphaColorRec.Red;
end;
timer := 1;
countdown := countdown - 1;
end;

// 顯示倒數次數
Percent.Text := Trunc(countdown).ToString;
end;



編輯記錄
P.D. 重新編輯於 2018-03-14 14:16:05, 註解 無‧
GrandRURU
站務副站長


發表:240
回覆:1680
積分:1874
註冊:2005-06-21

發送簡訊給我
#2 引用回覆 回覆 發表時間:2018-03-15 10:32:53 IP:59.120.xxx.xxx 未訂閱
Synchronize 表示執行點會在 Main Thread 上,在 Synchronize 下 Sleep,畫面會被冰凍起來

和在 Button 直接下 Sleep 的原理相同
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#3 引用回覆 回覆 發表時間:2018-03-15 18:14:25 IP:118.169.xxx.xxx 未訂閱
感謝RuRu兄提點, 
sleep 不是重點, 因為我拿掉, 結果還是一樣, 就是沒有辦法展現同步執行的效果!
===================引 用 GrandRURU 文 章===================
Synchronize 表示執行點會在 Main Thread 上,在 Synchronize 下 Sleep,畫面會被冰凍起來

和在 Button 直接下 Sleep 的原理相同
pcplayer99
尊榮會員


發表:146
回覆:790
積分:632
註冊:2003-01-21

發送簡訊給我
#4 引用回覆 回覆 發表時間:2018-03-15 23:06:00 IP:183.17.xxx.xxx 未訂閱
老兄,看得出来你是一点不懂啊。你的代码的问题是sync部分。另外,你的这种thread写法本身也怪怪的。 要么,用传统写法,写一个tthread的继承的子类。 要么,干脆用 ttask.run。这个所谓的异步,其实也是个thread。 至于sync部分,是同步到主thread,也就是阻塞当前thread,将代码丢给主thread,执行完毕才继续由当前thread来执行后继的代码。同步到主thread的目的是要修改界面上的东西比如label。否则没必要。 手机打字,没说清楚,见谅。
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#5 引用回覆 回覆 發表時間:2018-03-15 23:09:22 IP:118.169.xxx.xxx 未訂閱
我的確不懂, 之前從沒接觸過這方面的資訊, 也沒有人指導, 所以全部的內容都是由網路的程式摸索的, 謝謝指教!
努力學習中...
就上面原po的案例, 如何讓顯示器與我要進行的功能可以同時運行
舉凡連結資料庫, 開啟檔案, 儲存資料, 搜尋記錄, 開啟程式等等, 都需要有顯示在跑, 讓使用者知道程式正在運作中, 例如 Loading..., 會提出這個需求, 是因為在 10.1 我是用 fgx 是很棒的工具, 也發展了一系列的顯示功能, 但到了 10.2, 原來的 fgx 卻失效了, 不知道 10.2 到底改了什麼, 雖然 fgx 的原作有提供在 10.2 的解法, 但看過原作寫法, 無法套用現有我已開發的程式, 導致我無法使用 10.2 發展(這我在前幾次討論已有po文), 如果這個問題不解, 那便是發展只能到 10.1 了, 10.2 有很多 fix, 想用卻不能用, 著實令人頭痛及心灰意冷
===================引 用 pcplayer99 文 章===================
老兄,看得出来你是一点不懂啊。你的代码的问题是sync部分。另外,你的这种thread写法本身也怪怪的。 要么,用传统写法,写一个tthread的继承的子类。 要么,干脆用 ttask.run。这个所谓的异步,其实也是个thread。 至于sync部分,是同步到主thread,也就是阻塞当前thread,将代码丢给主thread,执行完毕才继续由当前thread来执行后继的代码。同步到主thread的目的是要修改界面上的东西比如label。否则没必要。 手机打字,没说清楚,见谅。
編輯記錄
P.D. 重新編輯於 2018-03-16 10:42:49, 註解 無‧
pcplayer99
尊榮會員


發表:146
回覆:790
積分:632
註冊:2003-01-21

發送簡訊給我
#6 引用回覆 回覆 發表時間:2018-03-16 16:34:13 IP:183.239.xxx.xxx 未訂閱
你的代码:
begin
TThread.Synchronize(nil, procedure
begin
begin
Label1.Text := i.ToString;
end);
end;
TMyThread = class(TThread)
procedure Execute; override;
TThread.Synchronize(当我需要去通知界面,比如 Label.Text := 'abcc' 时,必须同步一下);

do someting C....

end

);

//回到原来的线程(最大的情况是主线程)
do some thing D....


用上面的方法,要注意,主线程执行完 Do something A,然后开始执行 TTask.Run,不会等待 TTaskRun 里面的 do Someting B 执行完,马上执行 do something D。

然后 TTask.Run 里面是另外一个 Thread 在跑,它执行 do Something B... 然后阻塞,这个thread 停下来,等待主线程执行 TThread.Synchronize 里面的代码,完成后,Thread 继续执行 do someting C。

至于 do someting C 执行完毕和 do someting D 执行完毕,没有时间上的先后关系。因为一个是主线程,一个是 TTask.Run 的线程。


必须注意:写 Thread 的代码,你的脑袋里要非常清楚,这一段代码是被哪个 Thread 在跑。
GrandRURU
站務副站長


發表:240
回覆:1680
積分:1874
註冊:2005-06-21

發送簡訊給我
#7 引用回覆 回覆 發表時間:2018-03-18 00:12:04 IP:180.217.xxx.xxx 未訂閱
http://delphi.about.com/od/kbthread/a/thread-gui.htm
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#8 引用回覆 回覆 發表時間:2018-03-18 13:07:02 IP:118.169.xxx.xxx 未訂閱
感謝 RuRu副大, 感謝 pcplayer, 各位的提點
小弟對 thread 真的不熟, 原理大致了解, 但要實作就GG了, 說句大家常戲虐的話, 英文字母ABCD26個我都看得懂, 但湊到一起就是它懂我, 我不懂它了! 目前我想要的功能大致已有解, 已發表到此分享了,
http://delphi.ktop.com.tw/board.php?cid=31&fid=79&tid=110102

往後還會在Thread上找時間再多研究練功, 目前手上還很多工作要接續開發, 只要能暫時解除我的開發瓶頸, 我就要繼續往下了, 謝謝各位!

GrandRURU
站務副站長


發表:240
回覆:1680
積分:1874
註冊:2005-06-21

發送簡訊給我
#9 引用回覆 回覆 發表時間:2018-03-19 09:22:30 IP:59.120.xxx.xxx 未訂閱
看起來 Timer 就能完全達到您的需求,不用 TTimer 的原因是?
GrandRURU
站務副站長


發表:240
回覆:1680
積分:1874
註冊:2005-06-21

發送簡訊給我
#10 引用回覆 回覆 發表時間:2018-03-19 10:55:42 IP:59.120.xxx.xxx 未訂閱
procedure TForm1.Timer2Timer(Sender: TObject);
var i: Integer;
begin
  i := Timer2.Tag;
  Label1.Text := i.ToString;
  if Timer2.Tag = 10 then
  begin    Timer2.Enabeled := False;    Timer2.Tag := 0;    ActiveDialog1.Stop;
  end
  else    Timer2.Tag := Timer2.Tag + 1;
end;

begin
  ActiveDialog1.Start(...);
  Timer2.Tag := 1;
  Timer2.Interval := 500; // 也就是 Sleep(500)
  Timer2.Enabled := True;
end;

效果和您的 Thread 相同,而且不需要額外的 Thread 知識
編輯記錄
GrandRURU 重新編輯於 2018-03-19 15:17:41, 註解 無‧
pcplayer99
尊榮會員


發表:146
回覆:790
積分:632
註冊:2003-01-21

發送簡訊給我
#11 引用回覆 回覆 發表時間:2018-03-19 15:12:22 IP:120.237.xxx.xxx 未訂閱
这个 BBS 有 BUG 了。前面我写了一大堆如何使用 Thread 的东西,结果显示出来,内容丢了很多,完全看不明白在讲什么。我点【修改】,进入修改后,之前的内容又都在。
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#12 引用回覆 回覆 發表時間:2018-03-28 01:15:42 IP:118.169.xxx.xxx 未訂閱
RuRu副大

我想要達成的 ActiveDialog 的範圍很廣, 舉凡在 Form1 create form2 完成前, 可能下sqlconnect 前到 sqlconnect 完成的這段時間, 也可能在在跑某一迴圈(如下載料庫到 local 端等), 有可能由 不同的 fomr 中去呼叫, 也可能由 datamoudle 呼叫, 或者是 clientmodule, 甚至由 unit 中也會呼叫這個顯示, 使用 timer 很麻煩, 以前(在 10.1版之前), fgx 的元件就可以做到我要的需求, 而且我只要下 fgActiveatyDialog.Show ... 中間的程式 ...Hide 就能有這個提示畫面, 但 10.2 之後, 畫面出不來了, 雖然前面我 po 了利用 天使的程式解決thread問題, 但當我真正套用在我的程式上(因為我要將這組包成一個 procedure, 方便在程式各處可以直接呼叫使用), 但套上後, 畫面還是在上面提到的各種運作結束後才會出現, pc大大有mail修改的範例, 我也仔細研究照做, 還是沒辦辦法達到我要的結果, 目前還在努力的研究中(目前我又改回以 fgx 的元件為主想辦法看能不能解決, 現在透過 pc大的方法, 我可以把 fgx 在運作前顯示出來了, 但這個 thread 如果是 show(即start) ... 跑程式 ... hide(即stop), 那顯示也是出不來, 如果 hide 那端不加入, 就可看到顯示, 所以暫時沒輒! 再繼續努力, 謝謝!

ps. 如果副大願意的, 不知有沒有機會我找個時間見面, 請副大指點一二, 也順便把我的系統給副大看, 可能會比較有方向, pc大太遠了
===================引 用 GrandRURU 文 章===================
procedure TForm1.Timer2Timer(Sender: TObject);
var i: Integer;
begin
  i := Timer2.Tag;
  Label1.Text := i.ToString;
  if Timer2.Tag = 10 then
  begin    Timer2.Enabeled := False;    Timer2.Tag := 0;    ActiveDialog1.Stop;
  end
  else    Timer2.Tag := Timer2.Tag   1;
end;

begin
  ActiveDialog1.Start(...);
  Timer2.Tag := 1;
  Timer2.Interval := 500; // 也就是 Sleep(500)
  Timer2.Enabled := True;
end;

效果和您的 Thread 相同,而且不需要額外的 Thread 知識
編輯記錄
P.D. 重新編輯於 2018-03-28 01:46:17, 註解 無‧
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#13 引用回覆 回覆 發表時間:2018-03-28 01:19:25 IP:118.169.xxx.xxx 未訂閱
有可能使用到ktop內定的關鍵字句, 例如 ON CREATE 就是不能連在一起, 否則會不見, 諸如此類...
===================引 用 pcplayer99 文 章===================
这个 BBS 有 BUG 了。前面我写了一大堆如何使用 Thread 的东西,结果显示出来,内容丢了很多,完全看不明白在讲什么。我点【修改】,进入修改后,之前的内容又都在。
stacker_liew
中階會員


發表:59
回覆:168
積分:65
註冊:2004-05-17

發送簡訊給我
#14 引用回覆 回覆 發表時間:2018-03-29 02:59:45 IP:60.51.xxx.xxx 未訂閱
我都喜歡用TThread.Synchronize(TThread.CurrentThread, procedure...
===================引 用 P.D. 文 章===================
請問各位:

firemonkey 的執行緒(Threading) 要如何使用, 我參考網路很多文獻, 至少有3-5種寫法
有的用
TThread.CreateAnonymousThread;
TThread.Synchronize(nil, procedure
begin
.....
end);

也有直接用
TThread.Synchronize(TThread.CurrentThread, 要執行的procedure);
還有以Task 方式撰寫

恕小弟在這方面是第一次接觸, 我參考各方的設計想做出一個同時執行兩組的 procedure 功能, 但就是達不到同時運作
第1組 ActiveDialog 是一個轉圈的計數顯示, 第2組是一個簡單的迴圈(只是做測試用)
我想要在第2組跑迴圈時 for .. next 同時, 第1組計數顯示同時會轉圈, 當 第2組跑完, 計數器也停止
但加入thread 後, 永遠都是第2組跑完, 第1組才會有效果
我相信是我對Thrad不懂原理的原故, 所以想請各位先進指點一二, 謝謝!

這是我其中一種寫法__

procedure TForm1.Button1Click(Sender: TObject);
var i:integer;
FActivityDialogThread: TThread;
begin
// ActiveDialog 是第1組計數器, 我是以 TFrame 下去設置的
ActiveDialog1.timer := 100;
ActiveDialog1.countdown := 5;
FActivityDialogThread := TThread.CreateAnonymousThread(procedure
begin
try
TThread.Synchronize(nil, procedure
var i : integer;
begin
for i := 0 to 100 do
begin
sleep(50);
Label1.Text := i.ToString;
end;
end);
finally
end;
end);
FActivityDialogThread.FreeOnTerminate := False;
FActivityDialogThread.Start;
// Timer1 是放在Form1 上, 每100ms 會執行一次(就是要跑轉圈的特效)
Timer1.Enabled := true;
end;

// 每 100ms 執行一次
procedure TForm1.Timer1Timer(Sender: TObject);
begin
ActiveDialog1.ActiveRun;
end;

// 這是 TFrame 中的g執行碼部份
// 目前是沒有加入 Thread, 但我有試過, 把 ActiveRun 改為上述 Threading做法, 結果是一樣的
......
public
{ Public declarations }
timer : integer; // 指定 ms 間隔
countdown : integer; // 指定要倒數幾次
procedure ActiveRun;
......
procedure TActiveDialog.ActiveRun;
begin
if countdown = 0 then
begin
Circle.Fill.Color := TAlphaColorRec.Red;
Run.Stroke.Color := $FFE0E0E0;
exit;
end;

timer := timer 1;
if timer < 20 then
begin
Run.EndAngle := 360 / (100 / (timer * 5));
end
else
begin
Run.EndAngle := 0;
Run.StartAngle := -90;
if Circle.Fill.Color = $FFE0E0E0 then
begin
Circle.Fill.Color := TAlphaColorRec.Red;
Run.Stroke.Color := $FFE0E0E0;
end
else
begin
Circle.Fill.Color := $FFE0E0E0;
Run.Stroke.Color := TAlphaColorRec.Red;
end;
timer := 1;
countdown := countdown - 1;
end;

// 顯示倒數次數
Percent.Text := Trunc(countdown).ToString;
end;



stacker_liew
中階會員


發表:59
回覆:168
積分:65
註冊:2004-05-17

發送簡訊給我
#15 引用回覆 回覆 發表時間:2018-03-29 12:47:35 IP:60.51.xxx.xxx 未訂閱
這個網站上面貼出來的原始碼無法排版,看起來很吃力。
===================引 用 pcplayer99 文 章===================
老兄,看得出来你是一点不懂啊。你的代码的问题是sync部分。另外,你的这种thread写法本身也怪怪的。 要么,用传统写法,写一个tthread的继承的子类。 要么,干脆用 ttask.run。这个所谓的异步,其实也是个thread。 至于sync部分,是同步到主thread,也就是阻塞当前thread,将代码丢给主thread,执行完毕才继续由当前thread来执行后继的代码。同步到主thread的目的是要修改界面上的东西比如label。否则没必要。 手机打字,没说清楚,见谅。
系統時間:2024-05-18 15:00:41
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!