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

執行緒 Thread 執行的疑問?

缺席
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#1 引用回覆 回覆 發表時間:2007-01-04 14:09:11 IP:210.241.xxx.xxx 未訂閱
為什麼顯示的結果是
t3 t2
t3 t2

不是
t1
t1 t2
t3 t2

---------- 程式關鍵部分如下 -------------- (完整程式在附件)
<textarea class="delphi" rows="10" cols="60" name="code"> Unit1.pas type procedure WMCHANGE(var Msg: TMessage); message WM_CHANGE; var t1 , t2 : TtestThread; procedure TForm1.FormCreate(Sender: TObject); begin Memo1.Clear; t1:= TtestThread.Create(true); t1.Wnd := Form1.Handle; t1.P1:='t1'; t1.Resume; // <== 這裡不是會讓 Thread 啟動, Memo1 顯示 t1 t2:= TtestThread.Create(true); t2.Wnd := Form1.Handle; t2.P1:='t2'; t2.Resume; // <== 這裡不是會讓 Thread 啟動, Memo1 顯示 t1 t2 t1.Suspend; t1.P1:='t3'; t1.Resume; // <== 這裡不是會讓 Thread 啟動, Memo1 顯示 t3 t2 end; procedure TForm1.WMCHANGE(var Msg: TMessage); begin Memo1.Lines.Add(t1.P1 ' ' t2.P1); end; Unit2.pas 中 const WM_CHANGE = WM_USER 1973; type TtestThread = class(TThread) private { Private declarations } pP1 : String; fWnd: THandle; public property P1: string read pP1 write pP1; property Wnd: THandle read fWnd write fWnd; protected procedure Execute; override; end; procedure TtestThread.Execute; begin SendMessage(Wnd, WM_CHANGE, wParam(P1), lParam(P1)); end; </textarea>
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
附加檔案:459c9a07ebb80_t2.zip
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#2 引用回覆 回覆 發表時間:2007-01-04 14:40:56 IP:220.130.xxx.xxx 訂閱
我沒有實際去執行程式,不過..

你t1開始執行的時候t2沒有初始化吧?
在處理msg的時候呼叫函式就會有問題了
然後..Thread在Execute method執行完畢理論上就是死亡狀態..

順便貼一下delphi help給你看..

The thread function and any of the routines it calls have their own local variables, just like any other Object Pascal routines. These routines also can access any global variables. In fact, global variables provide a powerful mechanism for communicating between threads.
Sometimes, however, you may want to use variables that are global to all the routines running in your thread, but not shared with other instances of the same thread class. You can do this by declaring thread-local variables. Make a variable thread-local by declaring it in a threadvar section. For example,

threadvar

x : integer;

declares an integer type variable that is private to each thread in the application, but global within each thread.
The threadvar section can only be used for global variables. Pointer and Function variables can be thread variables. Types that use copy-on-write semantics, such as long strings don work as thread variables either.
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
John Wong
初階會員


發表:1
回覆:35
積分:32
註冊:2004-09-18

發送簡訊給我
#3 引用回覆 回覆 發表時間:2007-01-04 15:07:59 IP:218.103.xxx.xxx 未訂閱
原因在於兩個問題.
1. Execute中沒有使用while loop, 所以只執行了一次SendMessage後就完了, 不能resume. 所以Memo1中顯示只Add了兩行資料.試試過成這樣:
while not Terminated do begin
SendMessage(Wnd, WM_CHANGE, wParam(P1), lParam(P1));
suspend;
end;
而在TForm1.FormCreate中不要再suspend, 直接resume便可以.
2. 當application完成執行TForm1.FormCreate後, 才會有空處理那兩個WM_CHANGE的message, 而在處理的時候, T1.P1及T2.P1確實分別是't3'及't2'.
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#4 引用回覆 回覆 發表時間:2007-01-05 08:27:09 IP:61.219.xxx.xxx 未訂閱
實驗一
如 John Wong 兄所說修改, 拿掉 t1.Suspend, 如果 FormCreate 執行完成後才會處理 thread,
小弟將 FormCreate 改為 Button1Click, 是否碰到 t1.Resume, t2.Resume 就會去執行 thread ?
實際測試顯示的仍然是
t3 t2
t3 t2
實驗二
修改 Execute 實際測試顯示結果如上, 的仍然是錯的

------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#5 引用回覆 回覆 發表時間:2007-01-05 08:46:54 IP:61.219.xxx.xxx 未訂閱
將 thread 初始化 ??? 不太懂
小弟用 Google 搜尋 "delphi thread 初始化"
http://www.china-code.net/article/9/65/cct0L9ue2.html
http://big5.yesky.com/b5/www.yesky.com/20000319/35322.shtml
<textarea class="delphi" rows="10" cols="60" name="code"> constructor TMyThread.Create(CreateSuspended: Boolean); { inheritedCreate(CreateSuspended); Priority := tpIdle; } </textarea>

http://72.14.235.104/search?q=cache:G6SS0YSl9NcJ:thns.tsinghua.edu.cn/thnsebooks/ebook44/08.pdf delphi thread 初始化&hl=zh-TW&gl=tw&ct=clnk&cd=17
constructor Create(Col:TColor;XPos,YPos:Integer); 宣告在 public
小弟在 public 增加一行
<textarea class="delphi" rows="10" cols="60" name="code"> public constructor Create(CreateSuspended: Boolean); // 錯誤, Unsatisfied forward or external declaration: 'TtestThread.Create' constructor Create; // 錯誤, Unsatisfied forward or external declaration: 'TtestThread.Create' property P1: string read pP1 write pP1; property Wnd: THandle read fWnd write fWnd; </textarea><br /> 可否明說該如何初始化呢?
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#6 引用回覆 回覆 發表時間:2007-01-05 08:48:00 IP:61.219.xxx.xxx 未訂閱
供大家參考 (網路抓的文章排版亂了, 小弟重新排過)

第二部份 Delphi 編程的核心技術

第8章多綫程應用程序

在很多情况下,需要采用多綫程技術進行程序設計。例如,常用的字處理軟件Word,當
輸入文字的時候,Word同時進行拼寫和語法的檢驗,也就是將文檔中的詞語與詞庫中的詞語
進行比較,并對文檔中的語句進行語法分析。這些操作都比較耗費時間,但是我們在使用
Word的時候并没有感覺到輸入過程有明顯的滯後現象。這裏Word就采用了多綫程技術,其中
一個綫程接收輸入,另一個綫程進行拼寫和語法的檢驗。
本章主要對Delphi環境中開發多綫程應用程序進行探討。

8.1進程與綫程

進程是應用程序的執行實例,每個進程是由私有的虚擬地址空間、代碼、數據和其他各種
系統資源組成的。進程在運行過程中創建的資源隨着進程的終止而被銷毁,所使用的系統資源
在進程終止時被釋放或關閉。
綫程是進程内部的一個執行單元(如可以是一個函數或一個活躍的類對象等)。系統創建好
進程後,實際上就啓動執行了該進程的主執行綫程。主執行綫程終止了,進程也就隨之終止。
每一個進程至少有一個綫程(即主執行綫程,它無需由用户去主動創建,是由系統將應用
程序啓動後創建的),用户根據需要在應用程序中創建其他綫程,多個綫程并發地運行在同一
個進程中。一個進程中的所有綫程都在該進程的虚擬地址空間中,使用這些虚擬地址空間、全
局變量和系統資源,所以綫程之間的通訊要比進程之間的通訊容易得多。多綫程設計在實際中
的使用較為廣泛。

使用多綫程具有以下優點:
•由于CPU的處理速度比較快,可以使用户在做一件事情的時候還可以做另外一件事。比
如在有些殺毒軟件殺毒的時候,還可以通過菜單來瀏覧病毒清單。
•在多個CPU的情况下,可以充分利用硬件的優勢:將一個大任務分成幾個小任務由不同
的CPU來完成。
•可以為每個綫程設置優先級,調整工作的進度。
多綫程在幫助解决某些問題的同時,也有一些新的問題:
•濫用綫程容易使程序變得支離破碎,增加程序編寫的復雜度。
•在多個綫程對數據進行讀和寫操作的時候,數據的安全有效性可能會遭到破壞。
•有時如果頻繁地在綫程間切换會耗費大量的CPU時間,使得整個工作的處理時間延長了。
因此,進行多綫程的程序設計,要充分考慮到設計的可行性和安全性,發揮出并行運行的
高效性,最大限度地提高程序的效率。

8.2TThread類

在Delphi環境中,通過TThread類可以方便地編寫多綫程應用程序。
TThread類將WindowsAPI函數中關于多綫程方面的函數封裝到了一起。TThread類是一個
抽象類,不可以直接創建它的實例,但是可以創建它的派生類。

利用TThread類來編寫多綫程應用程序的一般步驟如下:
1)從TThread類派生出一個新的綫程類。
2)創建綫程對象。
3)設置綫程對象的屬性,比如優先級等。
4)根據具體情况挂起或唤醒綫程。
5)結束綫程。

從TThread類派生出一個新的綫程類的過程非常簡單:通過菜單File|New打開NewItems對
話框,選中New標簽中的ThreadObject項,按下OK按鈕,在接着彈出的NewThreadObject對
話框中輸入新的綫程類的名稱。通常綫程類的名稱以T開頭,以Thread結束,例如例程S8_1中
的TSquareThread。在NewThreadObject對話框對話框中輸入完畢後按下OK按鈕,Delphi會自
動生成一個TThread類的派生類,并將代碼保存在一個新的單獨的文件中。例如:

unit unit 2;
interface
uses
Classes;
type
TSquareThread=class(TThread)
private
{Private declarations}
protected
procedure Execute;override;
end;

implementation

{Important:Methods and properties of objects in VCL can only be used in a
method called using Synchronize, for example,
Synchronize(UpdateCaption);
and Update Caption could look like,
procedure TSquareThread.UpdateCaption;
begin
Form1.Caption:='Updatedinathread';
end;}

{TSquareThread}
procedureTSquareThread.Execute;
begin
{Placethreadcodehere}
end;

end.

下面就可以根據特殊的需要修改新的綫程類了。

TThread類有一個構造函數Create,函數原型如下:

constructorCreate(CreateSuspended:Boolean);

其中參數CreateSuspended為一個布爾類型的變量。如果設置為False,則綫程對象創建後
立即調用TThread類的另一個過程Execute,也就是立即開始執行綫程的操作;如果設置為True,
則綫程對象創建後,要調用過程Resume後綫程的操作才開始。

可以在TThread類的派生類中重新定義Create構造函數,用來對派生類中的一些屬性進行初
始化。

我們可以自己定義TThread類的派生類中的Execute過程,過程Execute中的代碼就是綫程要
做的工作。如果Execute過程執行完畢,則該綫程就結束了,并釋放綫程占用的棧空間。通常
在Execute過程的重載函數中要有一個repeat...until語句,如:

procedureTSquareThread.Execute;
begin
...
repeat
...
//具體的操作

untilTerminated;//判斷是否應該結束綫程
...
end;
其中Terminated為綫程類的一個屬性,用來標志是否應該結束綫程。
此外,TThread類還有以下的一些屬性和方法:
•Priority優先級屬性。可以設置綫程的優先級。
•ReturnValue返回值屬性。當綫程結束時返回給其他綫程的一個數值。
•Suspended挂起屬性。可以判斷綫程是否被挂起。
•ThreadID綫程標識號屬性。是整個系統中綫程的標識號。使用WindowsAPI函數的時候
該屬性非常有用。
•DoTerminate産生一個OnTerminate事件,但是不結束綫程的執行。
•Resume唤醒一個綫程繼續執行。
•Suspend挂起一個綫程。
•Synchronize由主VCL綫程調用的一個同步過程。
•Terminate將Terminated屬性設置為True,中止綫程的執行。
•WaitFor等待綫程的中止并返回ReturnValue屬性的數值。

8.3綫程的同步

由于Delphi不支持多綫程同時訪問可視對象庫(VCL),例如在窗體上繪圖,所以在編寫多
綫程程序訪問VCL的時候要特彆注意,只能逐個地實現對VCL的訪問。具體可以采用下面的兩
個方法:

1.對于一些具有鎖定功能的對象,可以在鎖定之後再進行具體操作
例如TCanvas類有一個Lock過程,可以阻止其他綫程在畫布上作圖。所以在綫程的執行過
程中,在調用畫布作圖之前,調用Lock過程將畫布鎖定。作圖過程完成之後,調用Unlock過程
解除鎖定。
注意:TCanvas還有一個只讀屬性LockCount。在調用Lock過程的時候,LockCount將逐漸
增加;當調用Unlock過程的時候,LockCount將逐漸减少。當LockCount為零時,其他綫程才可
以在畫布上作圖。
此外,TCustomWinSocket、TGraphicsObject和TRemoteDataModule等類也具有Lock和
UnLock過程。

2.使用Synchronize函數

TThread類的Synchronize過程原型如下:
typeTThreadMethod=procedureofobject;
procedureSynchronize(Method:TThreadMethod);
其中參數Method為一個不帶參數的過程名。在這個不帶參數的過程中是一些訪問VCL的
代碼。
可以在Execute過程中調用Synchronize過程來避免對VCL的并發訪問。程序運行期間的具
體過程實際上是由Synchronize過程來通知主綫程,然後主綫程在適當的時機執行Synchronize
過程的參數列表中個不帶參數的過程。在多個綫程的情况下,主綫程將Synchronize過程所發的
通知放到消息隊列中,然後逐個地響應這些消息。通過這種機制Synchronize實現了綫程之間的
同步。
所以,可以將對VCL訪問的代碼寫在一個不帶參數的過程中,然後將過程名作為
Synchronize過程的參數在Execute過程中進行調用。舉一個簡單的例子:
procedureTMyThread.PushTheButton;
begin
Button1.Click();
end;
procedureTMyThread.Execute;
begin
...
Synchronize(PushTheButton);
...
end;
例程S8_1也是通過第2種方法實現綫程間的同步的。

8.4綫程的優先級

在程序一開始運行的時候,系統會自動創建一個進程和一個主綫程。其中進程的優先級被
稱為基本優先級,綫程的優先級則默認與進程的優先級相同。
通過設置綫程對象的Priority屬性可以改變綫程的優先級。Priority屬性的定義如下:
typeTThreadPriority=(tpIdle,tpLowest,tpLower,tpNormal,tpHigher,
tpHighest,tpTimeCritical);
propertyPriority:TThreadPriority;
Priority屬性的取值及其含義如表8-1所示。
在應用程序中要合理地設置綫程的優先級。不要因為一些綫程的優先級很高而使得其他一
些綫程因為得不到CPU的處理時間而被“餓死”,也不要因為綫程的級彆都差不多而導致頻繁
切换花費大量的CPU時間。
例程S8_1該例程主要對下面的一些内容進行説明。
•多綫程技術的實現。
•綫程的同步。
•綫程的優先級設置。
•重載構造函數。

表8-1Priority屬性的取值及其含義

數值 含義
tpIdle 最低的優先級。只有系統處于空閑狀態時才執行
tpLowest比普通優先級低兩級
tpLower 比普通優先級低一級
tpNormal普通的優先級
tpHigher比普通優先級高一級
tpHighest比普通優先級高兩級
tpTimeCritical最高的優先級

例程S8_1的運行結果界面如圖8-1所示。在程序的窗體中有代表三種顔色的三個CheckBox
組件,當選中某個CheckBox組件後,程序會創建一個綫程,實現的功能是在窗體右邊同一行
的地方顯示一個具有相應顔色的移動的正方形。在窗體左邊還有三個Edit組件及其關聯的
UpDown組件,通過UpDown組件可以改變相應Edit組件中的數值,同時改變了當前行對應的綫
程的優先級。如果按下了窗體下部的“對齊(S)”按鈕,則將窗體中正在移動的正方形在左邊
對齊。

圖8-1例程S8_1運行結果界面

對于例程S8_1需要説明以下内容:
•在例程中,從TThread類中只派生出一個子類,對該子類創建了三個不同顔色的對象。
•由于關系到在窗體中作圖,所以必須協調好各綫程間的同步問題。我們采用的是調用
Synchronize過程的方式。
•表8-1中列出的綫程的優先值有7種,但是在本例程中為了便于在窗體界面上操作,對三
個新創建的子綫程進行一些限制,優先級只可以是tpLowest、tpLower和tpNormal三種,
不能高過主綫程的優先級。
•對齊按鈕實際上不可能總是將三個正方形嚴格地在左邊“對齊”,但是在大致對齊的情况
下可以更好地觀察效果。

具體實現過程如下:
1.創建工程
通過菜單File|NewApplication創建一個新的工程。
2.創建并定義TThread類的派生類
1)通過菜單File|New打開NewItems對話框,選中New標簽中的ThreadObject項,按下OK
按鈕。在接着彈出的對話框中輸入TThread類的派生類的名稱為:TSquareThread。將工程中的
所有文件保存在同一個目録下。
2)為了讓正方形在窗體中移動,定義X坐標上的兩個位置變量X1和X2,其中X1為舊的位
置,X2為新的位置。讓X1和X2的數值不停地加一或减一,就可以形成一個動態的圖像。至于
Y方向的坐標則與前面的CheckBox組件對齊。
另外,為了初始化這些數值,將構造函數進行重新定義。
為了畫圖的方便,還添加了一個Paint過程。Paint過程没有參數,在窗體中作圖。它將作
為Synchronize過程的參數被調用。

具體代碼如下:
unit unit2;
interface
uses
Classes,Graphics; //為TColor類而添加了Graphics
type

TSquareThread=class(TThread)
private
{Private declarations}
protected
procedure Execute; override;
procedure Paint; //在窗體中畫正方形
public
X1,Y,X2,Step:Integer; //正方形的新、舊位置和變化的步長
EColor:TColor; //正方形的顔色
constructor Create(Col:TColor;XPos,YPos:Integer); //構造函數
end;

implementation

uses unit1;
{Important: Methods and properties of objects in VCL can only beused in a
method called using Synchronize, for example,
...
procedure TSquareThread.Paint;
begin
//使用窗體的顔色在老地方畫一個正方形
Form1.Canvas.Pen.Color:=Form1.Color;
Form1.Canvas.Brush.Color:=Form1.Color;
Form1.Canvas.Rectangle(X1-5,Y-5,X1 5,Y 5);
//使用窗體的顔色在老地方畫一個正方形
Form1.Canvas.Pen.Color:=EColor;
Form1.Canvas.Brush.Color:=EColor;
Form1.Canvas.Rectangle(X2-5,Y-5,X2 5,Y 5);
end;

constructor TSquareThread.Create(Col:TColor;XPos,YPos:Integer);
begin
EColor:=Col; //顔色
X2:=XPos; //X坐標
X1:=X2;
Y:=YPos; //Y坐標
inheritedCreate(True);
end;
...

3)定義TSquareThread類的Execute過程如下:

procedureTSquareThread.Execute;
begin
{Placethreadcodehere}
Step:=1;//步長
repeat
X1:=X2;
X2:=X2 Step;
//改變X坐標位置
if X2<160 then
Step:=1 //到了左邊
else if X2>(Form1.ClientWidth-15) then
Step:=-1; //到了右邊
Synchronize(Paint);
untilTerminated;
end;

注意:對于正方形在水平方向移動的範圍可以大致進行估計:根據UpDown組件的Left屬
性和Width屬性,設置一個常量為左邊界。該例程中設置的左邊界為160,讀者可以根據實際情
况進行設置。

3.對主窗體進行設計

1)參考圖8-1在窗體Form1中添加組件,根據需要設置相應的屬性。
注意:
•一開始將三個CheckBox的Checked屬性設置為False。
•設置三個UpDown組件的Associate屬性,與對應的Edit組件關聯,并將UpDown組件的
Max屬性設置為4,Min屬性設置為2,Position屬性設置為4。Edit組件中的2、3和4分彆
對應的優先級為tpLowest、tpLower和tpNormal。
2)當選中某個CheckBox屬性後,則創建具有相應顔色屬性的一個綫程;當再次按下這個
CheckBox組件後,則結束該綫程。添加三個CheckBox組件的OnClick事件的處理過程如下:

unit unit1;
interface
uses
Windows,Messages,SysUtils,Classes,Graphics,Controls,Forms,Dialogs,
StdCtrls,ComCtrls,Unit2;
type
TForm1=class(TForm)
...
public
{Publicdeclarations}
Ball1,Ball2,Ball3:TSquareThread;
end;

...
//第一個綫程(紅色)
procedureTForm1.CheckBox1Click(Sender:TObject);
var
X,Y:Integer;
begin
if(CheckBox1.Checked)=Truethen
begin
X:=160; //移動範圍X坐標的左邊界
Y:=CheckBox1.Top CheckBox1.Height div 2; //Y坐標
Edit1.Text:='4';
//優先級對應為tpNormal
Ball1:=TSquareThread.Create(clRed,X,Y); //創建綫程
Ball1.Resume; //唤醒該綫程
end
else
Ball1.Terminate; //結束該綫程
end;

//第二個綫程(黄色)
procedure TForm1.CheckBox2Click(Sender:TObject);
var
X,Y:Integer;
begin
if (CheckBox2.Checked)=True then
begin
X:=160;
Y:=CheckBox2.Top CheckBox2.Height div 2;
Edit2.Text:='4';
Ball2:=TSquareThread.Create(clYellow,X,Y);
Ball2.Resume;
end
else
Ball2.Terminate;
end;

//第三個綫程(藍色)
procedureTForm1.CheckBox3Click(Sender:TObject);
var
X,Y:Integer;
begin
if (CheckBox3.Checked)=True then
begin
X:=160;
Y:=CheckBox3.Top CheckBox3.Heightdiv2;
Edit3.Text:='4';
Ball3:=TSquareThread.Create(clBlue,X,Y);
Ball3.Resume;
end
else
Ball3.Terminate;
end;

3)下面編寫代碼實現綫程優先級的設置。添加三個Edit組件的OnChange事件的處理過程
如下:
//第一個綫程(紅色)
procedureTForm1.Edit1Change(Sender:TObject);
begin
if((Edit1.Text='2')and(Ball1<>nil))then
Ball1.Priority:=tpLowest; //比tpNormal低兩級

if((Edit1.Text='3')and(Ball1<>nil))then
Ball1.Priority:=tpLower; //比tpNormal低一級

if((Edit1.Text='4')and(Ball1<>nil))then
Ball1.Priority:=tpNormal;
end;

//第二個綫程(黄色)
procedureTForm1.Edit2Change(Sender:TObject);
begin
if((Edit2.Text='2')and(Ball2<>nil))then
Ball2.Priority:=tpLowest;
if((Edit2.Text='3')and(Ball2<>nil))then
Ball2.Priority:=tpLower;
if((Edit2.Text='4')and(Ball2<>nil))then
Ball2.Priority:=tpNormal;
end;

//第三個綫程(藍色)
procedureTForm1.Edit3Change(Sender:TObject);
begin
if((Edit3.Text='2')and(Ball3<>nil))then
Ball3.Priority:=tpLowest;
if((Edit3.Text='3')and(Ball3<>nil))then
Ball3.Priority:=tpLower;
if((Edit3.Text='4')and(Ball3<>nil))then
Ball3.Priority:=tpNormal;
end;

4)添加Button1組件的OnClick事件的處理過程如下:
procedureTForm1.Button1Click(Sender:TObject);
begin
if((Ball1<>nil))then //第一個綫程(紅色)
Ball1.X2:=160;
if((Ball2<>nil))then //第二個綫程(黄色)
Ball2.X2:=160;
if((Ball3<>nil))then //第三個綫程(藍色)
Ball3.X2:=160;
end;

4.編譯、鏈接和運行程序,測試效果
如果輸入正確,程序將正常運行。

説明:

在例程S8_1中,如果三個CheckBox組件全部被選中,則該程序中存在四個綫程。其中主
綫程的優先級為tpNormal,與其他綫程創建的時候優先級一様。
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#7 引用回覆 回覆 發表時間:2007-01-05 10:04:19 IP:220.130.xxx.xxx 訂閱
  Memo1.Clear;
t1:= TtestThread.Create(true);
t1.Wnd := Form1.Handle;
t1.P1:='t1';
t1.Resume;
//在這裡t1.execute,form.wmchange被invoke,但是t2的初始化還在下面?
t2:= TtestThread.Create(true);//這就是初始化,也就是建構子的行為
t2.Wnd := Form1.Handle;
t2.P1:='t2';
t2.Resume;



procedure TForm1.WMCHANGE(var Msg: TMessage);
begin
Memo1.Lines.Add(t1.P1 ' ' t2.P1);
end;
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#8 引用回覆 回覆 發表時間:2007-01-05 11:27:11 IP:210.241.xxx.xxx 未訂閱
t1,t2 先初始化, 將 t1.Resume 改到 t2:= TtestThread.Create(true) 下面也是不行啊

<textarea class="delphi" rows="10" cols="60" name="code"> t1:= TtestThread.Create(true); t2:= TtestThread.Create(true); t1.Wnd := Form1.Handle; t1.P1:='t1'; t2.Wnd := Form1.Handle; t2.P1:='t2'; t1.Resume; t2.Resume; </textarea>
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#9 引用回覆 回覆 發表時間:2007-01-05 11:28:56 IP:210.241.xxx.xxx 未訂閱
請問 TtestThread.Execute 是在 t1:=TtestThread.Create(true) 時候被執行, 還是在 t1.Resume 時候被執行 ?

------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#10 引用回覆 回覆 發表時間:2007-01-05 11:33:30 IP:220.130.xxx.xxx 訂閱
建構子Thread.Create的參數是true時就是create & suspend, false就是create & start
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#11 引用回覆 回覆 發表時間:2007-01-05 11:44:13 IP:210.241.xxx.xxx 未訂閱
不好意思, 小弟還是沒弄懂盲點在哪 ?
<textarea class="delphi" rows="10" cols="60" name="code"> t1:= TtestThread.Create(true); t1.Wnd := Form1.Handle; t1.P1:='t1'; t1.Resume; // 在這裡t1.execute,form.wmchange被invoke,但是t2的初始化還在下面? // 有關係嗎 ??? t1 和 t2 是各自獨立的 thread, 都是 TtestThread 這個 Class 的一個 Instance // 這裡 t1.Resume => t1.execute => form.wmchange, 此時 t1.P1='t1' 啊 ??? // 如果說 form.wmchange 是因為在 form.OnCreate 執行後才能使用, 所以結果變成 t3 t2 // OnCreate 改成 OnClick, form.OnClick 和 form.wmchange 應該沒有先後關係了吧 ? // 但是結果仍是 t3 t2 t2:= TtestThread.Create(true);//這就是初始化,也就是建構子的行為 t2.Wnd := Form1.Handle; t2.P1:='t2'; t2.Resume; </textarea>
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#12 引用回覆 回覆 發表時間:2007-01-05 11:50:17 IP:220.130.xxx.xxx 訂閱
原本的t1和t2是沒關係的,但是t1送出訊息後,
procedure TForm1.WMCHANGE(var Msg: TMessage);
begin
Memo1.Lines.Add(t1.P1 ' ' t2.P1);
end;
這個函式會被呼叫,而這時候t2「理論上」仍未被初始化,但是在這裡卻使用了t2的成員

但是回到John Wong的說法,如果Create結束之後才會被處理,那麼先前的訊息已經送了,
那麼,很有可能造成t1跟t2分別已經被設值,等到repaint時就只會畫上t1跟t2的最終值
所以如果你想要控制VCL的動作,就如同說明上說的,加上sync來確保每一個動作執行是by order
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#13 引用回覆 回覆 發表時間:2007-01-05 12:23:07 IP:210.241.xxx.xxx 未訂閱
> 但是在這裡卻使用了t2的成員
小弟將程式改為 Memo1.Lines.Add(t1.P1); 避免用到t2, 結果印出兩個 t3
設定 t1.resume , t2.resume, Memo1.Lines.Add 當中斷點, 以 F7 單步執行追蹤, 執行順序居然是
<textarea class="delphi" rows="10" cols="60" name="code"> t1.resume; t2.resume; t1.P1:='t3'; t1.Resume; Memo1.Lines.Add(t1.P1); Memo1.Lines.Add(t1.P1); </textarea>
傻眼了 @_@
>8.3綫程的同步
>由于Delphi不支持多綫程同時訪問可視對象庫(VCL)
TTestThread.Execute 中只是送個訊息而已, 沒使用到 Memo1 吧 (所以不該有鎖住 Memo1 的行為吧?)
而 TForm1.WMCHANGE 也不是 Thread 的 Method, 它使用 Memo1 也沒必要鎖住

雖然可能是因為 "由于Delphi不支持多綫程同時訪問可視對象庫" , 小弟還是不知道錯誤盲點在哪 ?

------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#14 引用回覆 回覆 發表時間:2007-01-05 12:34:55 IP:61.219.xxx.xxx 未訂閱
因為 Synchronize 只能執行 thread 的 method , 不能直接些執行 SendMessage, 小弟改成這樣
請問該如何修正 ? 謝謝 ^_^

<textarea class="delphi" rows="10" cols="60" name="code"> type TtestThread = class(TThread) private { Private declarations } pP1 : String; pP2 : String; fWnd: THandle; public property P1: string read pP1 write pP1; property P2: string read pP2 write pP2; property Wnd: THandle read fWnd write fWnd; procedure SendMessage2(Wnd, WM_CHANGE, wParam(P1), lParam(P2)); //<= Missing parameter type protected procedure Execute; override; end; implementation procedure TtestThread.SendMessage2(Wnd, WM_CHANGE, wParam(P1), lParam(P1) ); // 修改 begin // 修改 SendMessage(Wnd, WM_CHANGE, wParam(P1), lParam(P1) ) // 修改 end; // 修改 procedure TtestThread.Execute; begin while not Terminated do begin Synchronize(SendMessage2(Wnd, WM_CHANGE, wParam(P1), lParam(P1) )); end; end;</textarea>
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#15 引用回覆 回覆 發表時間:2007-01-05 14:27:47 IP:220.130.xxx.xxx 訂閱
問這種問題真欠打..:P
你不覺得你的函式原型跟Delphi該有的函式格式不一樣嗎
compiler已經很明顯的告訴你問題是什麼了
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#16 引用回覆 回覆 發表時間:2007-01-05 14:53:29 IP:61.219.xxx.xxx 未訂閱
不行耶 ~ 還是顯示 t3 t2 , Synchronize 用法有誤嗎 ? ( 腦袋快一片混亂中 @_@ )
<textarea class="delphi" rows="10" cols="60" name="code"> procedure TtestThread.SendMessage2; begin SendMessage(Wnd, WM_CHANGE, wParam(P1), lParam(P2) ); end; procedure TtestThread.Execute; begin Synchronize(SendMessage2); end;</textarea>
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#17 引用回覆 回覆 發表時間:2007-01-05 15:04:26 IP:220.130.xxx.xxx 訂閱
你這樣的寫法只能確保「同時間只有一個人可以送訊息(使用函式)」
但是訊息是一定會被送出去的,只是訊息被處理的時候t1跟t2的狀態早就被改變了
所以你要想一下「如何送出訊息,讓這些訊息被紀錄」或是「送出訊息,並在訊息被處理完畢之前,t1跟t2都要等候訊息處理完畢才能繼續」
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
pcboy
版主


發表:177
回覆:1838
積分:1463
註冊:2004-01-13

發送簡訊給我
#18 引用回覆 回覆 發表時間:2007-01-05 16:52:51 IP:210.241.xxx.xxx 未訂閱
書到用時方恨少 , 智到用時少根筋
------
能力不足,求助於人;有能力時,幫幫別人;如果您滿意答覆,請適時結案!

子曰:問有三種,不懂則問,雖懂有疑則問,雖懂而想知更多則問!
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#19 引用回覆 回覆 發表時間:2007-01-05 17:27:58 IP:220.130.xxx.xxx 訂閱
你很強求一定要在create時完成嗎?
可以考慮在Activate時去作這件事就不會造成問題了,
或者是你可以參考之前有人說過的,找個事件塞你要的動作,
執行完在事件裡面把這個事件指向null
就不會執行第二次

thread的書delphi跟bcb都還有,可以去找來看
尤其是寫RS232之類的書都會有
如果你也懂其它語言的話,那麼thread的書是翻不完的
或者是google一下應該也是不少
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
John Wong
初階會員


發表:1
回覆:35
積分:32
註冊:2004-09-18

發送簡訊給我
#20 引用回覆 回覆 發表時間:2007-01-05 21:29:53 IP:218.103.xxx.xxx 未訂閱
即使改為由Button1Click執行, 照樣是執行完整個Button1Click, main thread才會去處理message. 當你send message到application時, 只是message放到message queue中, 並未即時處理. 要待application的完成手上的工作, 才會處理跟著下來的message. 解決的方法可以使用TApplication.ProcessMessages, 若要肯定執行TApplication.ProcessMessages時testthread已把message send到application中, 可以在TApplication.ProcessMessages之前加上sleep(100).
Coffee
版主


發表:31
回覆:878
積分:561
註冊:2006-11-15

發送簡訊給我
#21 引用回覆 回覆 發表時間:2007-01-05 23:44:25 IP:203.73.xxx.xxx 訂閱
果然是高手(逃)
------
不論是否我發的文,在能力範圍皆很樂意為大家回答問題。
為了補我的能力不足之處,以及讓答案可以被重複的使用,希望大家能儘量以公開的方式問問題。
在引述到我的文時自然會儘量替各位想辦法,謝謝大家!
系統時間:2024-04-25 10:01:30
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!