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

thread取得的值與全域變數的問題?

答題得分者是:aftcast
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#1 引用回覆 回覆 發表時間:2008-11-14 01:06:29 IP:122.146.xxx.xxx 訂閱
請教各位大大,小弟有各疑惑我用一個Thread去接收我送命令給另外一支程式AP回傳的值
回傳的格式會是COUNT1234,1234是我要的數值我會把值(1234)給全域的變數RetrunCount
並把flRetrunData 設成true 給function ShowMisData使用
最後是顯示在utform這個form的label(1234)
我確定我有收到COUNT1234也解析正確
只是在function ShowMisData內卻完全不知道flRetrunData已經是true與RetrunCount=1234
請大家為小弟指點一下迷津...thx
[code cpp]
unit utMain;

......
TClientUi2MisThread = class(TThread)
private
CB: TCommBlock2;
public
glRetrunCount : String;
flgHaveData : boolean;
procedure HandleInput;
protected
procedure Execute; override;
end;

var
frmMain : TfrmMain;
ClientUi2MisThread: TClientUi2MisThread;
Client2 : TIdTCPClient;
RetrunCount : String;
flRetrunData : boolean;
procedure TClientUi2MisThread.Execute;
var
strMsg:string;
begin
while not Terminated do
begin
try
if not Client2.Connected then
begin
Client2.Disconnect;
Client2.Connect(1000);
strMsg:='重新連線到 Ui2MisServer APP 成功。';
ShowMsg(strMsg);//StatusBar.Panels[4].Text := strMsg;
//Terminate
end
else
try
Client2.ReadBuffer(CB, Sizeof(CB));
Synchronize(HandleInput);
except
on E:Exception do
begin
if Client2.Connected then Client2.Disconnect;
//frmMain.tmAutoConnSvr.Enabled := True;
end;
end;
except
begin
strMsg := '重新連線到 Ui2MisServer AP 失敗。';
ShowMsg(strMsg);
sleep(2000);
continue;
end;
end;
end;
end;
procedure TClientUi2MisThread.HandleInput;
var
strMsg : string;
begin
if CB.Command = 'MESSAGE' then
ShowMsg(CB.MyName ' sends message : ' CB.Data)
else
//
// Read DATA
//
if CB.Command = 'DATA' then
begin
//ShowMsg(CB.MyName ' sends ver-data : ' CB.Data);
ShowMsg(CB.MyName ' sends data : ' CB.Data);
//
if Copy(CB.Data, 1, 5) = 'COUNT' then
begin
RetrunCount:=trim( Copy(CB.Data, 6, 10) );
ShowMsg('Get Data Record: ' RetrunCount);
//frmMain.HideSplashMsg;
flRetrunData := true;
end;
end;
end;
function TfrmMain.ShowMisData : integer;
var
tmpTime :integer;
begin
tmpTime:= 0;
Result := 0;
SendMisCmd('GET COUNT','MIS',QALL_Count,'','');
while (1=1) do
begin
if (flRetrunData = true) then
begin
Result:=strtoint(RetrunCount);
break;
end;
sleep(1000);
tmpTime := tmpTime 1;
if tmpTime >= 30 then
break;
end;
end;
procedure TfrmMain.SendMisCmd(const strCommand,strUseOdbc,strMisSql,strLocalSql,strMyName : string);
var
CB1 : TCommBlock2;
strWhere :string;
begin
CB1.Command := strCommand;
CB1.UseOdbc := strUseOdbc;
CB1.MisSql := strMisSql;
CB1.LocalSql := strLocalSql;
CB1.Data := '';
CB1.MyName := Client2.Socket.Binding.IP; //Client2.LocalName;
CB1.RecvName := '';
Client2.WriteBuffer(CB1, SizeOf(TCommBlock2(CB1)), True);
end;

unit utform;
procedure TfrmInform.FormCreate(Sender: TObject);
begin
label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);
end;

[/code]
編輯記錄
lkkplayer 重新編輯於 2008-11-14 01:09:07, 註解 無‧
syntax
尊榮會員


發表:26
回覆:1139
積分:1258
註冊:2002-04-23

發送簡訊給我
#2 引用回覆 回覆 發表時間:2008-11-14 09:49:21 IP:59.125.xxx.xxx 訂閱
看不懂,你要問什麼,重點在哪,也看不出來你的步驟(你要做什麼?)

只是在function ShowMisData內卻完全不知道flRetrunData已經是true與RetrunCount=1234 ????When and where ?

===================引 用 lkkplayer 文 章===================
請教各位大大,小弟有各疑惑我用一個Thread去接收我送命令給另外一支程式AP回傳的值
回傳的格式會是COUNT1234,1234是我要的數值我會把值(1234)給全域的變數RetrunCount
並把flRetrunData 設成true 給function ShowMisData使用
最後是顯示在utform這個form的label(1234)
我確定我有收到COUNT1234也解析正確
只是在function ShowMisData內卻完全不知道flRetrunData已經是true與RetrunCount=1234
請大家為小弟指點一下迷津...thx
[code cpp]
unit utMain;

......
TClientUi2MisThread = class(TThread)
private
CB: TCommBlock2;
public
glRetrunCount : String;
flgHaveData : boolean;
procedure HandleInput;
protected
procedure Execute; override;
end;

var
frmMain : TfrmMain;
ClientUi2MisThread: TClientUi2MisThread;
Client2 : TIdTCPClient;
RetrunCount : String;
flRetrunData : boolean;
procedure TClientUi2MisThread.Execute;
var
strMsg:string;
begin
while not Terminated do
begin
try
if not Client2.Connected then
begin
Client2.Disconnect;
Client2.Connect(1000);
strMsg:='重新連線到 Ui2MisServer APP 成功。';
ShowMsg(strMsg);//StatusBar.Panels[4].Text := strMsg;
//Terminate
end
else
try
Client2.ReadBuffer(CB, Sizeof(CB));
Synchronize(HandleInput);
except
on E:Exception do
begin
if Client2.Connected then Client2.Disconnect;
//frmMain.tmAutoConnSvr.Enabled := True;
end;
end;
except
begin
strMsg := '重新連線到 Ui2MisServer AP 失敗。';
ShowMsg(strMsg);
sleep(2000);
continue;
end;
end;
end;
end;
procedure TClientUi2MisThread.HandleInput;
var
strMsg : string;
begin
if CB.Command = 'MESSAGE' then
ShowMsg(CB.MyName ' sends message : ' CB.Data)
else
//
// Read DATA
//
if CB.Command = 'DATA' then
begin
//ShowMsg(CB.MyName ' sends ver-data : ' CB.Data);
ShowMsg(CB.MyName ' sends data : ' CB.Data);
//
if Copy(CB.Data, 1, 5) = 'COUNT' then
begin
RetrunCount:=trim( Copy(CB.Data, 6, 10) );
ShowMsg('Get Data Record: ' RetrunCount);
//frmMain.HideSplashMsg;
flRetrunData := true;
end;
end;
end;
function TfrmMain.ShowMisData : integer;
var
tmpTime :integer;
begin
tmpTime:= 0;
Result := 0;
SendMisCmd('GET COUNT','MIS',QALL_Count,'','');
while (1=1) do
begin
if (flRetrunData = true) then
begin
Result:=strtoint(RetrunCount);
break;
end;
sleep(1000);
tmpTime := tmpTime 1;
if tmpTime >= 30 then
break;
end;
end;
procedure TfrmMain.SendMisCmd(const strCommand,strUseOdbc,strMisSql,strLocalSql,strMyName : string);
var
CB1 : TCommBlock2;
strWhere :string;
begin
CB1.Command := strCommand;
CB1.UseOdbc := strUseOdbc;
CB1.MisSql := strMisSql;
CB1.LocalSql := strLocalSql;
CB1.Data := '';
CB1.MyName := Client2.Socket.Binding.IP; //Client2.LocalName;
CB1.RecvName := '';
Client2.WriteBuffer(CB1, SizeOf(TCommBlock2(CB1)), True);
end;

unit utform;
procedure TfrmInform.FormCreate(Sender: TObject);
begin
label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);
end;

[/code]
2007
中階會員


發表:54
回覆:90
積分:98
註冊:2008-08-12

發送簡訊給我
#3 引用回覆 回覆 發表時間:2008-11-14 10:27:24 IP:220.132.xxx.xxx 未訂閱

TfrmInform.FormCreate <--- TfrmInform 的建立(TfrmInform.FormCreate)是如何觸發的???


===================引 用 lkkplayer 文 章===================
請教各位大大,小弟有各疑惑我用一個Thread去接收我送命令給另外一支程式AP回傳的值
回傳的格式會是COUNT1234,1234是我要的數值我會把值(1234)給全域的變數RetrunCount
並把flRetrunData 設成true 給function ShowMisData使用
最後是顯示在utform這個form的label(1234)
我確定我有收到COUNT1234也解析正確
只是在function ShowMisData內卻完全不知道flRetrunData已經是true與RetrunCount=1234
請大家為小弟指點一下迷津...thx
[code cpp]
unit utMain;

......
TClientUi2MisThread = class(TThread)
private
CB: TCommBlock2;
public
glRetrunCount : String;
flgHaveData : boolean;
procedure HandleInput;
protected
procedure Execute; override;
end;

var
frmMain : TfrmMain;
ClientUi2MisThread: TClientUi2MisThread;
Client2 : TIdTCPClient;
RetrunCount : String;
flRetrunData : boolean;
procedure TClientUi2MisThread.Execute;
var
strMsg:string;
begin
while not Terminated do
begin
try
if not Client2.Connected then
begin
Client2.Disconnect;
Client2.Connect(1000);
strMsg:='重新連線到 Ui2MisServer APP 成功。';
ShowMsg(strMsg);//StatusBar.Panels[4].Text := strMsg;
//Terminate
end
else
try
Client2.ReadBuffer(CB, Sizeof(CB));
Synchronize(HandleInput);
except
on E:Exception do
begin
if Client2.Connected then Client2.Disconnect;
//frmMain.tmAutoConnSvr.Enabled := True;
end;
end;
except
begin
strMsg := '重新連線到 Ui2MisServer AP 失敗。';
ShowMsg(strMsg);
sleep(2000);
continue;
end;
end;
end;
end;
procedure TClientUi2MisThread.HandleInput;
var
strMsg : string;
begin
if CB.Command = 'MESSAGE' then
ShowMsg(CB.MyName ' sends message : ' CB.Data)
else
//
// Read DATA
//
if CB.Command = 'DATA' then
begin
//ShowMsg(CB.MyName ' sends ver-data : ' CB.Data);
ShowMsg(CB.MyName ' sends data : ' CB.Data);
//
if Copy(CB.Data, 1, 5) = 'COUNT' then
begin
RetrunCount:=trim( Copy(CB.Data, 6, 10) );
ShowMsg('Get Data Record: ' RetrunCount);
//frmMain.HideSplashMsg;
flRetrunData := true;
end;
end;
end;
function TfrmMain.ShowMisData : integer;
var
tmpTime :integer;
begin
tmpTime:= 0;
Result := 0;
SendMisCmd('GET COUNT','MIS',QALL_Count,'','');
while (1=1) do
begin
if (flRetrunData = true) then
begin
Result:=strtoint(RetrunCount);
break;
end;
sleep(1000);
tmpTime := tmpTime 1;
if tmpTime >= 30 then
break;
end;
end;
procedure TfrmMain.SendMisCmd(const strCommand,strUseOdbc,strMisSql,strLocalSql,strMyName : string);
var
CB1 : TCommBlock2;
strWhere :string;
begin
CB1.Command := strCommand;
CB1.UseOdbc := strUseOdbc;
CB1.MisSql := strMisSql;
CB1.LocalSql := strLocalSql;
CB1.Data := '';
CB1.MyName := Client2.Socket.Binding.IP; //Client2.LocalName;
CB1.RecvName := '';
Client2.WriteBuffer(CB1, SizeOf(TCommBlock2(CB1)), True);
end;

unit utform;
procedure TfrmInform.FormCreate(Sender: TObject);
begin
label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);
end;

[/code]
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#4 引用回覆 回覆 發表時間:2008-11-14 21:18:33 IP:220.129.xxx.xxx 訂閱
syntax大大:

不好意思,小弟寫的有點亂,我解釋一下我程式的順序
1.frmMain是我主程式的視窗(大略的程式如下),有個Button1會去開另一個視窗frmInform
2.當按下Button1開啟frmInform時,會觸發TfrmInform.FormCreate
3.FormCreate事件中會呼叫主程式(function)frmMain.ShowMisData
4.(function)frmMain.ShowMisData中SendMisCmd('GET COUNT','MIS',QALL_Count,'','');會送命令給另外一支程式AP
5.進入while迴圈,等待AP回送需要的資訊。
6.procedure TClientUi2MisThread.HandleInput;中會去接收資訊並解析RetrunCount,並把flRetrunData設成true

現在問題點來了,小弟原本以為在while迴圈,等待的時間中,TClientUi2MisThread.HandleInput應該就會接收到資訊
解析出RetrunCount,並把flRetrunData設成true
讓frmMain.ShowMisData這個function可以回傳值給視窗frmInform顯示出來

但是經小弟測試後,感覺進入while迴圈後好像把TClientUi2MisThread的主控權拿走了
等到我的計數器到了跳出while迴圈後,TClientUi2MisThread才又開始運作,
這個時候TClientUi2MisThread.HandleInput;才真的有接收到資訊解析出RetrunCount,並把flRetrunData設成true
這樣反而造成我這行程式label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);筆數永遠都是0

請大大為小弟指點一下迷津...thx

===================引 用 syntax 文 章===================
看不懂,你要問什麼,重點在哪,也看不出來你的步驟(你要做什麼?)

只是在function ShowMisData內卻完全不知道flRetrunData已經是true與RetrunCount=1234 ????When and where ?

===================引 用 lkkplayer 文 章===================
請教各位大大,小弟有各疑惑我用一個Thread去接收我送命令給另外一支程式AP回傳的值
回傳的格式會是COUNT1234,1234是我要的數值我會把值(1234)給全域的變數RetrunCount
並把flRetrunData 設成true 給function ShowMisData使用
最後是顯示在utform這個form的label(1234)
我確定我有收到COUNT1234也解析正確
只是在function ShowMisData內卻完全不知道flRetrunData已經是true與RetrunCount=1234
請大家為小弟指點一下迷津...thx
[code cpp]
unit utMain;

......
TClientUi2MisThread = class(TThread)
private
CB: TCommBlock2;
public
glRetrunCount : String;
flgHaveData : boolean;
procedure HandleInput;
protected
procedure Execute; override;
end;

var
frmMain : TfrmMain;
ClientUi2MisThread: TClientUi2MisThread;
Client2 : TIdTCPClient;
RetrunCount : String;
flRetrunData : boolean;

procedure TClientUi2MisThread.Execute;
var
strMsg:string;
begin
while not Terminated do
begin
try
if not Client2.Connected then
begin
Client2.Disconnect;
Client2.Connect(1000);
strMsg:='重新連線到 Ui2MisServer APP 成功。';
ShowMsg(strMsg);//StatusBar.Panels[4].Text := strMsg;
//Terminate
end
else
try
Client2.ReadBuffer(CB, Sizeof(CB));
Synchronize(HandleInput);
except
on E:Exception do
begin
if Client2.Connected then Client2.Disconnect;
//frmMain.tmAutoConnSvr.Enabled := True;
end;
end;
except
begin
strMsg := '重新連線到 Ui2MisServer AP 失敗。';
ShowMsg(strMsg);
sleep(2000);
continue;
end;
end;
end;
end;
procedure TClientUi2MisThread.HandleInput;
var
strMsg : string;
begin
if CB.Command = 'MESSAGE' then
ShowMsg(CB.MyName ' sends message : ' CB.Data)
else
//
// Read DATA
//
if CB.Command = 'DATA' then
begin
//ShowMsg(CB.MyName ' sends ver-data : ' CB.Data);
ShowMsg(CB.MyName ' sends data : ' CB.Data);
//
if Copy(CB.Data, 1, 5) = 'COUNT' then
begin
RetrunCount:=trim( Copy(CB.Data, 6, 10) );
ShowMsg('Get Data Record: ' RetrunCount);
//frmMain.HideSplashMsg;
flRetrunData := true;
end;
end;
end;
function TfrmMain.ShowMisData : integer;
var
tmpTime :integer;
begin
tmpTime:= 0;
Result := 0;
SendMisCmd('GET COUNT','MIS',QALL_Count,'','');
while (1=1) do
begin
if (flRetrunData = true) then
begin
Result:=strtoint(RetrunCount);
break;
end;
sleep(1000);
tmpTime := tmpTime 1;
if tmpTime >= 30 then
break;
end;
end;
procedure TfrmMain.SendMisCmd(const strCommand,strUseOdbc,strMisSql,strLocalSql,strMyName : string);
var
CB1 : TCommBlock2;
strWhere :string;
begin
CB1.Command := strCommand;
CB1.UseOdbc := strUseOdbc;
CB1.MisSql := strMisSql;
CB1.LocalSql := strLocalSql;
CB1.Data := '';
CB1.MyName := Client2.Socket.Binding.IP; //Client2.LocalName;
CB1.RecvName := '';
Client2.WriteBuffer(CB1, SizeOf(TCommBlock2(CB1)), True);
end;
------------------------------------------------------------------------------------------------------

unit utform;
procedure TfrmInform.FormCreate(Sender: TObject);
begin
label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);
end;

[/code]
2007
中階會員


發表:54
回覆:90
積分:98
註冊:2008-08-12

發送簡訊給我
#5 引用回覆 回覆 發表時間:2008-11-14 22:19:06 IP:218.170.xxx.xxx 未訂閱
lkkplayer 大大:

知道你的問題在那兒了,我以前也遇到,但還是沒找到答案!! 呵~~~ 會不會覺得這樣肥答粉xxxxx。

我以前也是用 while 迴圈,等待另一個程序的結束或一個值,但好像只要 while 一執行,就就就會一直執行至結束,

如:你程式中,若無 if tmpTime >= 30 then break;,真得是變成無窮迴圈,

也不知如何(用什麼語法),把執行權先釋放給別的正在等待執行權的程序,

執行緒真的不是粉好寫,也許功力不足吧,加上底子不深,沒去好好研究執行緒,半路出家真素累。

我想就讓別人幫我們找到答案吧!!!!!

===================引 用 lkkplayer 文 章===================
syntax大大:

不好意思,小弟寫的有點亂,我解釋一下我程式的順序
1.frmMain是我主程式的視窗(大略的程式如下),有個Button1會去開另一個視窗frmInform
2.當按下Button1開啟frmInform時,會觸發TfrmInform.FormCreate
3.FormCreate事件中會呼叫主程式(function)frmMain.ShowMisData
4.(function)frmMain.ShowMisData中SendMisCmd('GET COUNT','MIS',QALL_Count,'','');會送命令給另外一支程式AP
5.進入while迴圈,等待AP回送需要的資訊。
6.procedure TClientUi2MisThread.HandleInput;中會去接收資訊並解析RetrunCount,並把flRetrunData設成true

現在問題點來了,小弟原本以為在while迴圈,等待的時間中,TClientUi2MisThread.HandleInput應該就會接收到資訊
解析出RetrunCount,並把flRetrunData設成true
讓frmMain.ShowMisData這個function可以回傳值給視窗frmInform顯示出來

但是經小弟測試後,感覺進入while迴圈後好像把TClientUi2MisThread的主控權拿走了
等到我的計數器到了跳出while迴圈後,TClientUi2MisThread才又開始運作,
這個時候TClientUi2MisThread.HandleInput;才真的有接收到資訊解析出RetrunCount,並把flRetrunData設成true
這樣反而造成我這行程式label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);筆數永遠都是0

請大大為小弟指點一下迷津...thx
jow
尊榮會員


發表:66
回覆:751
積分:1253
註冊:2002-03-13

發送簡訊給我
#6 引用回覆 回覆 發表時間:2008-11-14 23:02:14 IP:123.193.xxx.xxx 未訂閱
矇著眼猜猜看

改用
BytesRead := Client2.Socket.Recv(CB, Sizeof(CB));
if BytesRead > 0 then
begin
end;

看看可否Work?
編輯記錄
jow 重新編輯於 2008-11-14 23:04:36, 註解 無‧
RootKit
資深會員


發表:16
回覆:358
積分:419
註冊:2008-01-02

發送簡訊給我
#7 引用回覆 回覆 發表時間:2008-11-15 00:35:29 IP:122.126.xxx.xxx 訂閱
原因:
當你 Synchronize(HandleInput); 時 Delphi 是調用 MainThread (Thread Window 由MainThread 創建)來處理 HandleInput
此時又因 ShowMisData 掉入無窮迴圈,導致 Synchronize(等待MainThread 回傳訊息) 無法切換至 MainThread。

解決:
可以嘗試將 SLEEP 改為 ProcessMessage,但也可能發生事件卡死的問題。
建議重新思考,誰主動通知誰被動。
ShowMisData 拆成只傳送命令 ->當Thread 處理完成後可 PostMessage 給主視窗依據帶入參數作判斷處理。
一來避免視窗呆掉,二來提高效率。

RootKit
資深會員


發表:16
回覆:358
積分:419
註冊:2008-01-02

發送簡訊給我
#8 引用回覆 回覆 發表時間:2008-11-15 00:58:55 IP:122.126.xxx.xxx 訂閱
補充:
使用 Synchronize 要非常小心,裡面的事件越簡單越好。以免干擾 MainThread 運作。

Synchronize 裡會 SendMessage (會等待訊息,Thread 此時是等待狀態)給由 MainThread 所建立的視窗Delphi 巧妙利用這點切換至MainThread
但因此時已進入 While 迴圈中,導致
由 MainThread 所建立的視窗不會即時接收到訊息(MainThread 沒空閒處理Windows訊息)。
這就所以當 ShowMisData TimeOut 結束時,才會收到的原因。

一般可能會用 CreateEvent 及WaitForMultipleObjects 來處理這類的問題。

lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#9 引用回覆 回覆 發表時間:2008-11-15 10:08:40 IP:218.169.xxx.xxx 訂閱
jow 大大:
我試用一下您的方法
我將ShowMisData這個function改寫如下:
結果還是接收不到值,另外我再加Sleep程式就直接當掉了
是我誤會您的意思了嗎?
[code cpp]
function TfrmMain.ShowMisData : integer;
var
tmpTime :integer;
BytesRead : integer;
CB : TCommBlock2;
begin
tmpTime:= 0;
Result := 0;
SendMisCmd('GET COUNT','MIS2',QALL_T0NBRK07_Count,'','');
//Sleep(1000);
BytesRead := Client2.Socket.Recv(CB, Sizeof(CB));
if BytesRead > 0 then
begin
Result:=strtoint(RetrunCount);
ShowMsg('等待ok');
end;
end;
[/code]
===================引 用 jow 文 章===================
矇著眼猜猜看

改用
BytesRead := Client2.Socket.Recv(CB, Sizeof(CB));
if BytesRead > 0 then
begin
end;

看看可否Work?
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#10 引用回覆 回覆 發表時間:2008-11-15 10:15:37 IP:218.169.xxx.xxx 訂閱
RootKit大大:

我想試試看看CreateEvent 及WaitForMultipleObjects 的方法,只是我不知道從哪裡著手??

===================引 用 RootKit 文 章===================
補充:
使用 Synchronize 要非常小心,裡面的事件越簡單越好。以免干擾 MainThread 運作。

Synchronize 裡會 SendMessage (會等待訊息,Thread 此時是等待狀態)給由 MainThread 所建立的視窗Delphi 巧妙利用這點切換至MainThread
但因此時已進入 While 迴圈中,導致
由 MainThread 所建立的視窗不會即時接收到訊息(MainThread 沒空閒處理Windows訊息)。
這就所以當 ShowMisData TimeOut 結束時,才會收到的原因。

一般可能會用 CreateEvent 及WaitForMultipleObjects 來處理這類的問題。

lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#11 引用回覆 回覆 發表時間:2008-11-15 11:00:17 IP:218.169.xxx.xxx 訂閱
RootKit大大:

如果ShowMisData只傳命令->當Thread 處理完成後PostMessage 給主視窗依據帶入參數作判斷處理。
那我另外一個視窗frmInform可以先停下來,等待筆數回傳回來,那該怎麼處理呢?
先前以為是可以用call function(ShowMisData)的方法那它停在fuction那邊等值傳回來
但現在看起來好像不可能,有比較好的方法嗎?
另外想請教說我之前有用過SendMessage作為兩支程式的溝通,PostMessage 是相同的用法嗎?有啥差別?
現在是在同一支程式做溝通,這樣會不會影響到Thread的控制權壓~~

[code cpp]

procedure TfrmInform.FormCreate(Sender: TObject);
begin
label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]); <-停下來等待
//下方還有一些程式碼需要用到筆數?

end;

[/code]

===================引 用 RootKit 文 章===================
原因:
當你 Synchronize(HandleInput); 時 Delphi 是調用 MainThread (Thread Window 由MainThread 創建)來處理 HandleInput
此時又因 ShowMisData 掉入無窮迴圈,導致 Synchronize(等待MainThread 回傳訊息) 無法切換至 MainThread。

解決:
可以嘗試將 SLEEP 改為 ProcessMessage,但也可能發生事件卡死的問題。
建議重新思考,誰主動通知誰被動。
ShowMisData 拆成只傳送命令 ->當Thread 處理完成後可 PostMessage 給主視窗依據帶入參數作判斷處理。
一來避免視窗呆掉,二來提高效率。

aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#12 引用回覆 回覆 發表時間:2008-11-15 11:52:11 IP:59.115.xxx.xxx 訂閱
你的程式架構很不順,晚點再深入的討論…

先用你原來的方法來使它功能正常,請把你的程式碼改成如下

while (1=1) do
begin
if (CheckSynchronize(1) = false) then continue; //加入這一行
if (flRetrunData = true) then



我正打算要寫一篇關於thread的所有議題,等ok了再po上來給大家參考
------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2008-11-15 11:55:55, 註解 無‧
aftcast 重新編輯於 2008-11-15 11:58:26, 註解 無‧
aftcast 重新編輯於 2008-11-15 12:03:11, 註解 無‧
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#13 引用回覆 回覆 發表時間:2008-11-15 13:08:30 IP:59.115.xxx.xxx 訂閱
RootKit 說的觀念很正確,但唯Synchronize 裡會 SendMessage 這裡是不對的。

事實上Synchronize 是用PostMessage然後post一個WM_NULL給TApplication的WndProc,WndProc收這個個msg後,就會去執行CheckSynchronize。
此外,當Synchronize執行完PostMessage後會再執行一個WaitForSingleObject的api,故使得thread暫時呈現suspend的狀態,直到…MainThread把你交待的工作完成時,WaitForSingleObject反回,而Synchronize方法也返回,thread也因此又再繼續下去…

PostMessage WaitForSingleObject 的二個動作是很像SendMessage,但還是有所不同的!

補充: 事實上篇我請提問者修改加入一行CheckSynchronize,其實改加入Application.ProcessMessages,也應該是可行的,試看看。


===================引 用 RootKit 文 章===================

Synchronize 裡會 SendMessage (會等待訊息,Thread 此時是等待狀態)給由 MainThread 所建立的視窗Delphi 巧妙利用這點切換至MainThread
但因此時已進入 While 迴圈中,導致
由 MainThread 所建立的視窗不會即時接收到訊息(MainThread 沒空閒處理Windows訊息)。
這就所以當 ShowMisData TimeOut 結束時,才會收到的原因。




------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#14 引用回覆 回覆 發表時間:2008-11-15 13:24:32 IP:218.169.xxx.xxx 訂閱
aftcast大大:

我試用了您的方法可以了耶!!@@
這是為什麼?用CheckSynchronize(1)判斷是否繼續?不太瞭解這個函式,另外那個參數(1)有什麼差別嗎?
真的是太神奇了~~~
我對Thread的瞭解連皮毛都算不上,請aftcast大大指導一下我的程式架構,我是看不太出來我的程式架構哪不順??
我終於可以先去吃飯了@@~~~
PS:CheckSynchronize是同步處理的意思嗎?


===================引 用 aftcast 文 章===================
你的程式架構很不順,晚點再深入的討論…

先用你原來的方法來使它功能正常,請把你的程式碼改成如下

while (1=1) do
begin
if (CheckSynchronize(1) = false) then continue; //加入這一行
if (flRetrunData = true) then



我正打算要寫一篇關於thread的所有議題,等ok了再po上來給大家參考
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#15 引用回覆 回覆 發表時間:2008-11-15 13:33:34 IP:218.169.xxx.xxx 訂閱
aftcast大大:
我剛剛繼續試用了一下,加Application.ProcessMessages
也是可以耶~~雖然都可以達到目的但是兩者有什麼差別嗎?

===================引 用 lkkplayer 文 章===================
aftcast大大:

我試用了您的方法可以了耶!!@@
這是為什麼?用CheckSynchronize(1)判斷是否繼續?不太瞭解這個函式,另外那個參數(1)有什麼差別嗎?
真的是太神奇了~~~
我對Thread的瞭解連皮毛都算不上,請aftcast大大指導一下我的程式架構,我是看不太出來我的程式架構哪不順??
我終於可以先去吃飯了@@~~~
PS:CheckSynchronize是同步處理的意思嗎?


===================引 用 aftcast 文 章===================
你的程式架構很不順,晚點再深入的討論…

先用你原來的方法來使它功能正常,請把你的程式碼改成如下

while (1=1) do
begin
if (CheckSynchronize(1) = false) then continue; //加入這一行
if (flRetrunData = true) then



我正打算要寫一篇關於thread的所有議題,等ok了再po上來給大家參考
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#16 引用回覆 回覆 發表時間:2008-11-15 14:58:45 IP:59.115.xxx.xxx 訂閱
CheckSynchronize這個函數算是一個較隱密少人知的函數。一般來說是不可能直接被我們叫用。它只有當Application收到WM_NULL時,或是當Application Idle的時候才會自動的被叫用。

我請你加了這一行其實是很暴力的直接叫用。其實你可以把那行改成Application.ProcessMessages; 這樣也可以間接的叫用CheckSynchronize,而這樣的使用方式應該也比較安全。用暴力法事實上是為了讓更多人了解細節,必竟單看ProcessMessages這行會誤會其真正的用意…

參數(1)中的1是指timeout的時間,不寫也是可以。設定1只是想讓你的while不會跑太快,有點像是sleep的意味。而CheckSynchronize並非只是"檢查",它更是"執行"你給的工作。不要被Check這個字給騙了!

此外,若要知道一點詳情,請看上篇我回rootkit的文。

你的架構問題,我本想好好的說明一下,但想一想我也不是很清楚你整個程式的目的,故可能無法很正確完整的重構。我只針對幾點說明。
1/ 你的procedure TClientUi2MisThread.HandleInput; 裡面看不出有什麼 none thread safe的function。 除了ShowMsg這個函數比較可疑,因為你也沒列出它是這麼樣子的。如果說ShowMsg也沒用到什麼vcl元件,或者你乾脆把它換成MessageBox,那麼你就不需要使用
Synchronize(HandleInput);
直接改成
HandleInput(); //沒用vcl就無需synchronize
要知道用Synchronize是很不得已的事,因為它讓效能變很差。

2/ 事實上我也不知道為何你會需要使用thread來處理收資料。也許你有你的理由…但若是單純的情形下,照你使用ShowMisData 的狀態,其實也許不需要thread。為什麼? 因為當你按下button後,產生一個form,from的create又去叫ShowMisData,而ShowMisData本身又進入一個loop裡… 這時候,注意一個現象: 對使用者來說按下button後,可能就進入了一個等待的狀態,直到新form完全的收到資料並展現答案。那麼試想,我們可否把thread裡的工作直接放到create事件裡?? 這樣就不需要thread了。反正對使用者來說,感覺是一樣的。我用概念寫一下流程
FormCreate裡
--> SendMisCmd --> Client2.ReadBuffer --> RetrunCount:=trim( Copy(CB.Data, 6, 10) ) --> label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);

3/ 如果真的有需要使用thead,那麼使用CreateEvent WaitForSingle(Multiple)Object 的方式才是最佳的。

以上的架構僅提供參考,也許不完全適用你現在的情形。最重要的還是功能要正常!!

PS這幾天我會找時間整理一下關於thread的所有議題內容,可能會把心得與教學放到我的blog並貼文至ktop。到時候再請多指教了!

------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2008-11-15 15:03:44, 註解 無‧
aftcast 重新編輯於 2008-11-15 15:05:21, 註解 無‧
aftcast 重新編輯於 2008-11-15 15:08:03, 註解 無‧
RootKit
資深會員


發表:16
回覆:358
積分:419
註冊:2008-01-02

發送簡訊給我
#17 引用回覆 回覆 發表時間:2008-11-15 16:00:10 IP:122.126.xxx.xxx 訂閱
不懂不要亂講。
我講的是 D5 你講的是 D7。

===================引 用 aftcast 文 章===================
RootKit 說的觀念很正確,但唯Synchronize 裡會 SendMessage 這裡是不對的。

事實上Synchronize 是用PostMessage然後post一個WM_NULL給TApplication的WndProc,WndProc收這個個msg後,就會去執行CheckSynchronize。
此外,當Synchronize執行完PostMessage後會再執行一個WaitForSingleObject的api,故使得thread暫時呈現suspend的狀態,直到…MainThread把你交待的工作完成時,WaitForSingleObject反回,而Synchronize方法也返回,thread也因此又再繼續下去…

PostMessage WaitForSingleObject 的二個動作是很像SendMessage,但還是有所不同的!

補充: 事實上篇我請提問者修改加入一行CheckSynchronize,其實改加入Application.ProcessMessages,也應該是可行的,試看看。


===================引 用 RootKit 文 章===================

Synchronize 裡會 SendMessage (會等待訊息,Thread 此時是等待狀態)給由 MainThread 所建立的視窗Delphi 巧妙利用這點切換至MainThread
但因此時已進入 While 迴圈中,導致
由 MainThread 所建立的視窗不會即時接收到訊息(MainThread 沒空閒處理Windows訊息)。
這就所以當 ShowMisData TimeOut 結束時,才會收到的原因。




編輯記錄
RootKit 重新編輯於 2008-11-15 16:03:02, 註解 無‧
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#18 引用回覆 回覆 發表時間:2008-11-15 16:27:39 IP:59.115.xxx.xxx 訂閱
受教了,原來D5之前是SendMessage。不過也是可以理解,二種的做法概念接近!

謝謝RootKit指正。



===================引 用 RootKit 文 章===================
不懂不要亂講。
我講的是 D5 你講的是 D7。


------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2008-11-15 16:28:26, 註解 無‧
RootKit
資深會員


發表:16
回覆:358
積分:419
註冊:2008-01-02

發送簡訊給我
#19 引用回覆 回覆 發表時間:2008-11-15 16:29:20 IP:122.126.xxx.xxx 訂閱
針對讀取或寫入共用部分直接用 EnterCriticalSection 段處理不要透過 Synchronize 繞一大圈。
如:
EnterCriticalSection(RWLock);
try
ReadBuffer..... or WriteBuffer
finally
LeaveCriticalSection(RWLock);
end;
處理顯示部分

這樣就比較單純化。繼續使用 SLEEP 大計。


剛才跟別人干架心情不好,對 aftcast 語氣不好的地方見諒
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#20 引用回覆 回覆 發表時間:2008-11-15 17:48:47 IP:61.223.xxx.xxx 訂閱
RootKit大大:
您指的是這一段程式的改寫嗎?

[code cpp]
procedure TClientUi2MisThread.Execute;
..........
Client2.ReadBuffer(CB, Sizeof(CB);
Synchronize(HandleInput);
............
[/code]


===================引 用 RootKit 文 章===================
針對讀取或寫入共用部分直接用 EnterCriticalSection 段處理不要透過 Synchronize 繞一大圈。
如:
EnterCriticalSection(RWLock);
try
ReadBuffer..... or WriteBuffer
finally
LeaveCriticalSection(RWLock);
end;
處理顯示部分

這樣就比較單純化。繼續使用 SLEEP 大計。


剛才跟別人干架心情不好,對 aftcast 語氣不好的地方見諒
編輯記錄
lkkplayer 重新編輯於 2008-11-15 17:49:35, 註解 無‧
lkkplayer 重新編輯於 2008-11-15 17:51:22, 註解 無‧
lkkplayer
一般會員


發表:26
回覆:59
積分:17
註冊:2006-11-22

發送簡訊給我
#21 引用回覆 回覆 發表時間:2008-11-15 18:16:27 IP:61.223.xxx.xxx 訂閱
aftcast大大:
1.none thread safe的function,我不太清楚您指的是?ShowMsg只是單純把訊息寫到畫面的一個Memo,讓我可以看到程式進行到哪裡。
至於你的建議,我倒是沒有想過,因為前人就已經是用Thread接收資料
FormCreate--> SendMisCmd --> Client2.ReadBuffer --> RetrunCount:=trim( Copy(CB.Data, 6, 10) ) --> label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);
可以用while把Client2.ReadBuffer 包起來接收資料嗎?
2.CreateEvent WaitForSingle(Multiple)Object 又聽到這個啦!!Rootkit大大也有這樣子建議,只是小弟不才
不知道如何直接使用在我目前的程式,有什麼建議的文章或範例可以參考嗎?

PS:aftcast大大的關於thread的所有議題內容大作.......期待中..

===================引 用 aftcast 文 章===================
CheckSynchronize這個函數算是一個較隱密少人知的函數。一般來說是不可能直接被我們叫用。它只有當Application收到WM_NULL時,或是當Application Idle的時候才會自動的被叫用。

我請你加了這一行其實是很暴力的直接叫用。其實你可以把那行改成Application.ProcessMessages; 這樣也可以間接的叫用CheckSynchronize,而這樣的使用方式應該也比較安全。用暴力法事實上是為了讓更多人了解細節,必竟單看ProcessMessages這行會誤會其真正的用意…

參數(1)中的1是指timeout的時間,不寫也是可以。設定1只是想讓你的while不會跑太快,有點像是sleep的意味。而CheckSynchronize並非只是"檢查",它更是"執行"你給的工作。不要被Check這個字給騙了!

此外,若要知道一點詳情,請看上篇我回rootkit的文。

你的架構問題,我本想好好的說明一下,但想一想我也不是很清楚你整個程式的目的,故可能無法很正確完整的重構。我只針對幾點說明。
1/ 你的procedure TClientUi2MisThread.HandleInput; 裡面看不出有什麼 none thread safe的function。 除了ShowMsg這個函數比較可疑,因為你也沒列出它是這麼樣子的。如果說ShowMsg也沒用到什麼vcl元件,或者你乾脆把它換成MessageBox,那麼你就不需要使用
Synchronize(HandleInput);
直接改成
HandleInput(); //沒用vcl就無需synchronize
要知道用Synchronize是很不得已的事,因為它讓效能變很差。

2/ 事實上我也不知道為何你會需要使用thread來處理收資料。也許你有你的理由…但若是單純的情形下,照你使用ShowMisData 的狀態,其實也許不需要thread。為什麼? 因為當你按下button後,產生一個form,from的create又去叫ShowMisData,而ShowMisData本身又進入一個loop裡… 這時候,注意一個現象: 對使用者來說按下button後,可能就進入了一個等待的狀態,直到新form完全的收到資料並展現答案。那麼試想,我們可否把thread裡的工作直接放到create事件裡?? 這樣就不需要thread了。反正對使用者來說,感覺是一樣的。我用概念寫一下流程
FormCreate裡
--> SendMisCmd --> Client2.ReadBuffer --> RetrunCount:=trim( Copy(CB.Data, 6, 10) ) --> label.Caption := format('MIS筆數: %d',[frmMain.ShowMisData]);

3/ 如果真的有需要使用thead,那麼使用CreateEvent WaitForSingle(Multiple)Object 的方式才是最佳的。

以上的架構僅提供參考,也許不完全適用你現在的情形。最重要的還是功能要正常!!

PS這幾天我會找時間整理一下關於thread的所有議題內容,可能會把心得與教學放到我的blog並貼文至ktop。到時候再請多指教了!

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