使用Thread當處理資料一多時,CPU的使用率就會到100%,怎樣可以修改這樣的情況? |
答題得分者是:william
|
shihchin
一般會員 發表:10 回覆:12 積分:4 註冊:2002-12-18 發送簡訊給我 |
想請問一下,我目前在Thread裡使用ADO對資料庫做更新的動作時,CLIENT一直送資料來我的CPU使用率就會到100%,想請問我應該怎樣更改才不會這樣,底下是我的程式碼: unit UDPServerMain; interface uses
Windows, Messages, Graphics, Controls, Forms, Dialogs, IdWinsock2, stdctrls,
SysUtils, Classes, IdBaseComponent, IdAntiFreezeBase, IdAntiFreeze,
IdComponent, IdUDPBase, IdUDPClient, IdStack, IdUDPServer, IdSocketHandle,
Grids, DBGrids, DB, ADODB, DBTables; type
TUDPMainForm = class(TForm)
SourceGroupBox: TGroupBox;
HostNameLabel: TLabel;
HostAddressLabel: TLabel;
HostName: TLabel;
HostAddress: TLabel;
UDPServer: TIdUDPServer;
UDPAntiFreeze: TIdAntiFreeze;
PortLabel: TLabel;
Port: TLabel;
BufferSizeLabel: TLabel;
BufferSize: TLabel;
UDPMemo: TMemo;
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
procedure FormCreate(Sender: TObject);
procedure UDPServerUDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
private
{ Private declarations }
public
{ Public declarations }
end; type
TServerThead = class(TThread)
private
DataStringStream: TStringStream;
procedure DatabaseProcess;
protected
procedure Execute; override;
public
constructor Create(APData: TStream);
end; var
UDPMainForm: TUDPMainForm; implementation const
HOSTNAMELENGTH = 80; {$R *.DFM} procedure TUDPMainForm.FormCreate(Sender: TObject);
begin
HostName.Caption := UDPServer.LocalName;
HostAddress.Caption := GStack.LocalAddress;
Port.Caption := IntToStr(UDPServer.DefaultPort);
BufferSize.Caption := IntToStr(UDPServer.BufferSize);
UDPServer.Active := True;
end; procedure TUDPMainForm.UDPServerUDPRead(Sender: TObject; AData: TStream; ABinding: TIdSocketHandle);
begin
TServerThead.Create(AData);
end; constructor TServerThead.Create(APData: TStream);
begin
DataStringStream := TStringStream.Create('');
DataStringStream.CopyFrom(APData, APData.Size);
FreeOnTerminate := True;
inherited Create(False);
end; procedure TServerThead.Execute;
begin
Synchronize(DatabaseProcess);
FreeOnTerminate := True;
end; procedure TServerThead.DatabaseProcess;
var
temp1,temp2:string;
ADOConnection:TADOConnection;
ADOQuery:TADOQuery;
begin
temp1:=Copy(DataStringStream.DataString,1,5);
temp2:=Copy(DataStringStream.DataString,6,Length(DataStringStream.DataString)-5);
UDPMainForm.UDPMemo.Lines.Add('Received "' DataStringStream.DataString );
ADOConnection:= TADOConnection.Create(nil);
ADOConnection.ConnectionString:=UDPMainForm.ADOConnection1.ConnectionString;
ADOConnection.LoginPrompt:=false;
ADOConnection.Connected:=true;
ADOQuery:= TADOQuery.Create(nil);
ADOQuery.Connection:=ADOConnection;
ADOQuery.Prepared:=true;
ADOQuery.Close();
ADOQuery.SQL.Clear;
ADOQuery.SQL.Add('select * from ab');
ADOQuery.SQL.Add('where b = ' #39 temp2 #39);
ADOQuery.Open;
if ADOQuery.RecordCount>0 then
begin
ADOQuery.Close;
ADOQuery.SQL.Clear;
ADOQuery.SQL.Add('update ab');
ADOQuery.SQL.Add('set a = ' #39 temp1 #39);
ADOQuery.SQL.Add('where b = ' #39 temp2 #39);
ADOQuery.Open;
end
else
begin
ADOQuery.Append;
ADOQuery.FieldByName('a').AsString:=temp1;
ADOQuery.FieldByName('b').AsString:=temp2;
ADOQuery.Post;
end;
ADOQuery.Close();
ADOQuery.Free;
ADOConnection.Connected:=false;
ADOConnection.Free;
DataStringStream.Free;
end; end.
|
william
版主 發表:66 回覆:2535 積分:3048 註冊:2002-07-11 發送簡訊給我 |
I think you have misused thread here. You Synchorize in your thread's execute method and it will actullay use the main thread in running the method DatabaseProcess. Perhaps you should not use synchorize and really use the thread... In this case you should call CoInitialize/CoUnInitialize yourself and create all the ADO component inside the thread.
|
shihchin
一般會員 發表:10 回覆:12 積分:4 註冊:2002-12-18 發送簡訊給我 |
|
william
版主 發表:66 回覆:2535 積分:3048 註冊:2002-07-11 發送簡訊給我 |
This should be normal since your thread is using the otherwise idle cpu time to process. BTW, VCL is not thread safe and your code about accessing the form's controls should be 'synchorized'. Perhaps you could set your thread as a lower priority on givig other process more resource.
|
shihchin
一般會員 發表:10 回覆:12 積分:4 註冊:2002-12-18 發送簡訊給我 |
|
william
版主 發表:66 回覆:2535 積分:3048 註冊:2002-07-11 發送簡訊給我 |
引言: 我將Priority設為最低的tpIdle還是相同的情況. >>< face="Verdana, Arial, Helvetica"> This is normal since you never sleep in your thread. There is no loop in your thread, so no need to sleep anyway. In Windows, you cannot specify how much cpu time you want to put on the thread, Windows will allocate the resource itself. IMHO, making this thread a lower priority should be good enough to make the system responsive to the user. Perhaps you could try async connect mode of the ADO (personally I never try). BTW, depends on you DB opening/closing connection may be time consuming. |
elase
一般會員 發表:4 回覆:23 積分:10 註冊:2003-06-05 發送簡訊給我 |
應該是因為動態建立 TADOConnection 的關
你可以在 Form 建立一個固定的 TADOConnection
在 Thread 建立 TADOQuery 時連向那個 TADOConnection
procedure TServerThead.DatabaseProcess; var temp1,temp2:string; ADOQuery:TADOQuery; begin temp1:=Copy(DataStringStream.DataString,1,5); temp2:=Copy(DataStringStream.DataString,6,Length(DataStringStream.DataString)-5); UDPMainForm.UDPMemo.Lines.Add('Received "' DataStringStream.DataString ); ADOQuery:= TADOQuery.Create(nil); ADOQuery.Connection:=UDPMainForm.ADOConnection; ADOQuery.Prepared:=true; ADOQuery.Close(); ADOQuery.SQL.Clear; ADOQuery.SQL.Add('select * from ab'); ADOQuery.SQL.Add('where b = ' #39 temp2 #39); ADOQuery.Open; if ADOQuery.RecordCount>0 then begin ADOQuery.Close; ADOQuery.SQL.Clear; ADOQuery.SQL.Add('update ab'); ADOQuery.SQL.Add('set a = ' #39 temp1 #39); ADOQuery.SQL.Add('where b = ' #39 temp2 #39); ADOQuery.Open; end else begin ADOQuery.Append; ADOQuery.FieldByName('a').AsString:=temp1; ADOQuery.FieldByName('b').AsString:=temp2; ADOQuery.Post; end; ADOQuery.Close(); ADOQuery.Free; ADOConnection.Connected:=false; DataStringStream.Free; end;甚至連 TADOQuery 也可以用共用的 利用 Therad 同步處理來控制 同步的方法: uses SyncObjs; procedure TServerThead.DatabaseProcess; var temp1,temp2:string; LockXY:TCriticalSection; begin LockXY.Create; LockXY.Acquire; UDPMainForm.ADOQuery.Close(); UDPMainForm.ADOQuery.SQL.Clear; UDPMainForm.ADOQuery.SQL.Add('select * from ab'); UDPMainForm.ADOQuery.SQL.Add('where b = ' #39 temp2 #39); UDPMainForm.ADOQuery.Open; if ADOQuery.RecordCount>0 then begin UDPMainForm.ADOQuery.Close; UDPMainForm.ADOQuery.SQL.Clear; UDPMainForm.ADOQuery.SQL.Add('update ab'); UDPMainForm.ADOQuery.SQL.Add('set a = ' #39 temp1 #39); UDPMainForm.ADOQuery.SQL.Add('where b = ' #39 temp2 #39); UDPMainForm.ADOQuery.Open; end else begin UDPMainForm.ADOQuery.Append; UDPMainForm.ADOQuery.FieldByName('a').AsString:=temp1; UDPMainForm.ADOQuery.FieldByName('b').AsString:=temp2; UDPMainForm.ADOQuery.Post; end; LockXY.Release; LockXY.Free; end; |
shihchin
一般會員 發表:10 回覆:12 積分:4 註冊:2002-12-18 發送簡訊給我 |
我之前雖然CPU使用率是100%,卻不會妨礙我的程式做其他的動作,例如點選BUTTON,MEMO輸入資料....等,可是我使用了你的方法後,雖然CPU的使用率降低到70%左右,但我在點選BUTTON或MEMO輸入資料,卻反應很慢,感覺比我之前更像是CPU使用率到100%.
引言: 應該是因為動態建立 TADOConnection 的關 你可以在 Form 建立一個固定的 TADOConnection 在 Thread 建立 TADOQuery 時連向那個 TADOConnectionprocedure TServerThead.DatabaseProcess; var temp1,temp2:string; ADOQuery:TADOQuery; begin temp1:=Copy(DataStringStream.DataString,1,5); temp2:=Copy(DataStringStream.DataString,6,Length(DataStringStream.DataString)-5); UDPMainForm.UDPMemo.Lines.Add('Received "' DataStringStream.DataString ); ADOQuery:= TADOQuery.Create(nil); ADOQuery.Connection:=UDPMainForm.ADOConnection; ADOQuery.Prepared:=true; ADOQuery.Close(); ADOQuery.SQL.Clear; ADOQuery.SQL.Add('select * from ab'); ADOQuery.SQL.Add('where b = ' #39 temp2 #39); ADOQuery.Open; if ADOQuery.RecordCount>0 then begin ADOQuery.Close; ADOQuery.SQL.Clear; ADOQuery.SQL.Add('update ab'); ADOQuery.SQL.Add('set a = ' #39 temp1 #39); ADOQuery.SQL.Add('where b = ' #39 temp2 #39); ADOQuery.Open; end else begin ADOQuery.Append; ADOQuery.FieldByName('a').AsString:=temp1; ADOQuery.FieldByName('b').AsString:=temp2; ADOQuery.Post; end; ADOQuery.Close(); ADOQuery.Free; ADOConnection.Connected:=false; DataStringStream.Free; end;甚至連 TADOQuery 也可以用共用的 利用 Therad 同步處理來控制 同步的方法:uses SyncObjs; procedure TServerThead.DatabaseProcess; var temp1,temp2:string; LockXY:TCriticalSection; begin LockXY.Create; LockXY.Acquire; UDPMainForm.ADOQuery.Close(); UDPMainForm.ADOQuery.SQL.Clear; UDPMainForm.ADOQuery.SQL.Add('select * from ab'); UDPMainForm.ADOQuery.SQL.Add('where b = ' #39 temp2 #39); UDPMainForm.ADOQuery.Open; if ADOQuery.RecordCount>0 then begin UDPMainForm.ADOQuery.Close; UDPMainForm.ADOQuery.SQL.Clear; UDPMainForm.ADOQuery.SQL.Add('update ab'); UDPMainForm.ADOQuery.SQL.Add('set a = ' #39 temp1 #39); UDPMainForm.ADOQuery.SQL.Add('where b = ' #39 temp2 #39); UDPMainForm.ADOQuery.Open; end else begin UDPMainForm.ADOQuery.Append; UDPMainForm.ADOQuery.FieldByName('a').AsString:=temp1; UDPMainForm.ADOQuery.FieldByName('b').AsString:=temp2; UDPMainForm.ADOQuery.Post; end; LockXY.Release; LockXY.Free; end; |
elase
一般會員 發表:4 回覆:23 積分:10 註冊:2003-06-05 發送簡訊給我 |
不清楚你的實際情況為何
看看你要不要用 TIdTCPServer
它有支援Thread 只要使用 TIdThreadMgrDefault 原件 server 程式
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, IdThreadMgr, IdThreadMgrPool, IdBaseComponent, IdComponent, IdTCPServer, IdThreadMgrDefault, StdCtrls, DB, ADODB, SyncObjs; type TForm1 = class(TForm) IdTCPServer1: TIdTCPServer; IdThreadMgrDefault1: TIdThreadMgrDefault; ADOConnection1: TADOConnection; ADOQuery1: TADOQuery; procedure IdTCPServer1Execute(AThread: TIdPeerThread); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread); var temp1,temp2:string; LockXY:TCriticalSection; begin temp1:=AThread.Connection.ReadLn(); temp2:=AThread.Connection.ReadLn(); LockXY:=TCriticalSection.Create; LockXY.Acquire; ADOQuery1.Close(); ADOQuery1.SQL.Clear; ADOQuery1.SQL.Add('select * from ab'); ADOQuery1.SQL.Add('where b = ' #39 temp2 #39); ADOQuery1.Open; if ADOQuery1.RecordCount>0 then begin ADOQuery1.Close; ADOQuery1.SQL.Clear; ADOQuery1.SQL.Add('update ab'); ADOQuery1.SQL.Add('set a = ' #39 temp1 #39); ADOQuery1.SQL.Add('where b = ' #39 temp2 #39); ADOQuery1.ExecSQL; end else begin ADOQuery1.SQL.Text := 'insert into ab values (' #39 temp1 #39 ',' #39 temp2 #39 ')'; ADOQuery1.ExecSQL; end; LockXY.Release; LockXY.Free; end; end.Client 程式 unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Button1: TButton; IdTCPClient1: TIdTCPClient; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); begin IdTCPClient1.Connect(); IdTCPClient1.WriteLn(edit1.Text); IdTCPClient1.WriteLn(edit2.Text); IdTCPClient1.Disconnect; end; end. |
william
版主 發表:66 回覆:2535 積分:3048 註冊:2002-07-11 發送簡訊給我 |
Hi elase, may I ask you why you choose to use a local variable as the critical section here > class="code">{ global/shared variable }
gCS: TCriticalSection;
{ threads }
Thread1,Thread2: TMyThread;
{ ... }
procedure TMyThread.Execute;
begin
{ ... }
gCS.Enter;
try
{ ... }
finally
gCS.Leave;
end;
{ ... }
end;
|
shihchin
一般會員 發表:10 回覆:12 積分:4 註冊:2002-12-18 發送簡訊給我 |
|
allanchou
一般會員 發表:9 回覆:35 積分:8 註冊:2003-04-01 發送簡訊給我 |
|
elase
一般會員 發表:4 回覆:23 積分:10 註冊:2003-06-05 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |