PDA

View Full Version : مشكل با Thread ها



MOJTABAATEFEH
چهارشنبه 09 دی 1388, 14:04 عصر
سلام دوستان عزيز من جستجو كردم اما چيزي راجع به موضوع مورد نظرم پيدا نكردم
من از Thread جاهاي مختلف استفاده كردم اما ايندفعه به يك مشكل برخورد كردم اينكه تا الان از پروسيجر در ترد استفاده مي كردم بدون پارامتر حالا چطور مي تونم از تابع و پروسيجر (با پارامتر) استفاده كنم Synchronize براي تابع يا پروسيجر با پارامتر كار نمي كنه چكار بايد كرد؟

با تشكر

vcldeveloper
چهارشنبه 09 دی 1388, 17:43 عصر
خب، مجبور نیستید از Synchronize استفاده کنید، از روش های دیگه Synchronization مثل CriticalSection می تونید استفاده کنید، یا پارامترهای مورد نظر را بصورت فیلدهای کلاس تعریف کنید.
یک حالت دیگه هم این هست که در دلفی 2009 و نسخه های بالاتر از Anonymous method به عنوان پارامتر ارسالی به متد Synchronize یا Queue استفاده کنید. یک مثال ساده اش اینطوری هست:

unit Unit2;

interface

uses
SysUtils, Classes;

type
TTestThread = class(TThread)
private
FField : string;
protected
procedure Execute; override;
end;

implementation

uses Unit1;

{ TTestThread }

procedure TTestThread.Execute;
var
i: Integer;
begin
FreeOnTerminate := True;

FField := 'Test';
for i := 1 to 1000 do
begin
Sleep(100);
Queue(procedure
begin
Form1.Edit1.Text := FField + IntToStr(i);
end);
end;
end;

end.
کد بالا یک Anonymous Method به متد Queue ارسال میکنه که در داخل خودش از یک متغیر محلی (i) و یک فیلد کلاس (FField) برای تغییر متن یک کنترل Edit در Form1 استفاده میکنه.

MOJTABAATEFEH
پنج شنبه 10 دی 1388, 00:24 صبح
موردي كه من قصد انجام آن را دارم مثال زير است كه يك پروسيجر و يك تابع داريم و هر د و دار اي پارامتر مي باشند اين دستورات بدون كار با Thread مشكلي ندارند لطفا نحوه پياده سازي با Thread را بفرماييد

با تشكر




var

DataList : TStringlist;
doc : IXMLDOMDocument;
root, child, child1 : IXMLDomElement;
text1, text2 : IXMLDOMText;
nlist : IXMLDOMNodelist;
dataRecord : String;


procedure TForm1.travelChildren(nlist1:IXMLDOMNodeList);
var
j:Integer;
temp:String;
begin
for j:=0 to nlist1.Get_length-1 do begin

if((nlist1.Get_item(j).Get_nodeType= 1)
or (nlist1.Get_item(j).Get_nodeType=5)) then
travelChildren
(nlist1.Get_item(j).Get_childNodes)

else if(nlist1.Get_item(j).Get_nodeType=3) then
begin
temp:= trim(nlist1.Get_item(j).Get_nodeValue);

dataRecord:=dataRecord+','+temp;

DataList.Add(temp);
end
end;
end;

function TForm1.insertintotable(stpt:TStringList):Integer;
var
i:Integer;
begin
table1.close;
table1.open;
table1.Insert;
for i := 0 to stpt.Count - 1 do begin
table1.Fields[i].AsVariant:= stpt[i];
end;
try
table1.post;
result:=1;
except
on E:Exception do
result:=-1;
end;
end;



procedure TForm1.Button2Click(Sender: TObject);
var
i,ret_val,count : Integer;
strData : String;
begin

try
count:=1;
DataList:=TStringList.Create;
memo1.Clear;
doc := CreateOleObject('Microsoft.XMLDOM')
as IXMLDomDocument;

doc.load('country.xml');
nlist:=doc.getElementsByTagName('Records');
memo1.lines.append('Table Name :country');
memo1.lines.append('---------------------');
for i:=0 to nlist.Get_length-1 do begin
travelChildren
(nlist.Get_item(i).Get_childNodes);

strData:=copy(dataRecord,2,length(dataRecord));
memo1.lines.append(strData);
dataRecord:='';
ret_val:=insertintotable(Datalist);
if ret_val=1 then
memo1.lines.append
('Data inserted successfully.............!')
else if ret_val=-1 then
memo1.lines.append
('Error while updating.....Try again.....!');
memo1.lines.append
('=======(Record no. :'+inttostr(count)+')');
DataList.Clear;
count:=count+1;
end;
except
on e:Exception do
Showmessage(e.message);
end;
end;

vcldeveloper
پنج شنبه 10 دی 1388, 00:41 صبح
خب، این که کار خاصی نداره، شما می تونید همه این متغیرهای عمومی را بصورت فیلد کلاس برای کلاس Threadتان تعریف کنید، توابع TravelChildren و InsertIntoTable را هم به عنوان متدهای همان کلاس تعریف کنید. کد مربوط به Button2Click را هم در متد Execute اون Thread بنویسید، با این تفاوت که کدهای مربوط به تغییر رابط کاربر (مثل کدهای نوشتن در Memo) را از آن خارج کنید.
در نهایت کدهای مربوط به تغییر در رابط کاربر را می تونید در یک یا چند متد بنویسید، و آنها را با استفاده از Synchronize یا Queue فراخوانی کنید.

البته در این مورد خاص، چون دارید از اشیاء COM برای کار با XML استفاده می کنید، باید در ابتدای متد Execute تابع CoInitialize را فراخوانی کنید، و در پایان آن تابع CoUninitialize. این دو تابع در یونیت ActiveX قرار دارند. درباره این دو تابع قبلا توضیح داده شده.

MOJTABAATEFEH
جمعه 11 دی 1388, 10:19 صبح
خب، این که کار خاصی نداره، شما می تونید همه این متغیرهای عمومی را بصورت فیلد کلاس برای کلاس Threadتان تعریف کنید، توابع TravelChildren و InsertIntoTable را هم به عنوان متدهای همان کلاس تعریف کنید. کد مربوط به Button2Click را هم در متد Execute اون Thread بنویسید، با این تفاوت که کدهای مربوط به تغییر رابط کاربر (مثل کدهای نوشتن در Memo) را از آن خارج کنید.
در نهایت کدهای مربوط به تغییر در رابط کاربر را می تونید در یک یا چند متد بنویسید، و آنها را با استفاده از Synchronize یا Queue فراخوانی کنید.

البته در این مورد خاص، چون دارید از اشیاء COM برای کار با XML استفاده می کنید، باید در ابتدای متد Execute تابع CoInitialize را فراخوانی کنید، و در پایان آن تابع CoUninitialize. این دو تابع در یونیت ActiveX قرار دارند. درباره این دو تابع قبلا توضیح داده شده.

جناب كشاورز ممنون بابت جوابتون من CoInitialize را جستجو كردم فقط دو مورد پيدا شد كه يكي همين بحث و ديگري هم مشابه اينجا از كلمه CoInitialize استفاده شده بود و توضيحي نبود لطفا راجع به استفاده از CoInitialize و CounInitialize كمي توضيح دهيد

با تشكر

vcldeveloper
جمعه 11 دی 1388, 11:39 صبح
لطفا راجع به استفاده از CoInitialize و CounInitialize كمي توضيح دهيد
درباره چگونگی استفاده شون و چرایی استفاده شون در پست قبل توضیح دادم.

جزئییاتش در تاپیک فعلی به کار شما نمیاد؛ خلاصه اش این هست که اشیاء COM باید برای هر Thread مقداردهی اولیه بشند. برای Thread اصلی این کار توسط Application.Initialize بطور خودکار انجام میشه. Threadهای دیگه باید خودشون این کار را انجام بدند.

MOJTABAATEFEH
جمعه 11 دی 1388, 12:22 عصر
درباره چگونگی استفاده شون و چرایی استفاده شون در پست قبل توضیح دادم.

جزئییاتش در تاپیک فعلی به کار شما نمیاد؛ خلاصه اش این هست که اشیاء COM باید برای هر Thread مقداردهی اولیه بشند. برای Thread اصلی این کار توسط Application.Initialize بطور خودکار انجام میشه. Threadهای دیگه باید خودشون این کار را انجام بدند.

CoInitialize داراي پارامتر هست با اون بايد چكار كرد؟
لطفا يك مثال بزنيد

با تشكر

vcldeveloper
جمعه 11 دی 1388, 12:25 عصر
CoInitialize داراي پارامتر هست با اون بايد چكار كرد؟
nil

-------------

MOJTABAATEFEH
جمعه 11 دی 1388, 18:32 عصر
من به صورت زير Thread رو نوشتم و استفاده كردم آيا روش درسته؟



Thread :

unit Thread;

interface

uses
Classes, ADODB,MSXML_TLB,comobj, SysUtils,ActiveX,dialogs;

type
mk = class(TThread)
private
{ Private declarations }
DataList: TStringlist;
doc: IXMLDOMDocument;
nlist: IXMLDOMNodelist;
dataRecord: String;
dele:tadoquery;

procedure travelChildren(nlist1:IXMLDOMNodeList);
Function insertintotable(stpt:TStringList):integer;

protected
procedure Execute; override;
end;

implementation

uses Unit1;

procedure mk.travelChildren(nlist1:IXMLDOMNodeList);
var
j:Integer;
temp:String;
begin
for j:=0 to nlist1.Get_length-1 do begin

if((nlist1.Get_item(j).Get_nodeType= 1)
or (nlist1.Get_item(j).Get_nodeType=5)) then
travelChildren(nlist1.Get_item(j).Get_childNodes)

else if(nlist1.Get_item(j).Get_nodeType=3) then
begin
temp:= trim(nlist1.Get_item(j).Get_nodeValue);

dataRecord:=dataRecord+','+temp;
DataList.Add(temp);
end
end;
end;

Function mk.insertintotable(stpt:TStringList):integer;
var
i:Integer;
begin
dele.close;
dele.open;
dele.Insert;
for i := 0 to stpt.Count - 1 do begin
dele.Fields[i].AsVariant:= stpt[i];
end;
try
dele.post;
result:=1;
except
on E:Exception do
result:=-1;
end;
end;


procedure mk.Execute;
var
i,ret_val,count : Integer;
strData : String;
begin
coinitialize(nil);
try
count:=1;
DataList:=TStringList.Create;
doc := CreateOleObject('Microsoft.XMLDOM') as IXMLDomDocument;

doc.load('cash.xml');
nlist:=doc.getElementsByTagName('Records');
for i:=0 to nlist.Get_length-1 do begin
travelChildren(nlist.Get_item(i).Get_childNodes);

strData:=copy(dataRecord,2,length(dataRecord));
dataRecord:='';
insertintotable(Datalist);
couninitialize;

DataList.Clear;
count:=count+1;
showmessage('Anjam Shod');
end;
except
on e:Exception do
Showmessage(e.message);
end;
terminate;
end;

end.


استفاده :

uses thread;

procedure TForm1.Button2Click(Sender: TObject);
var
ml:mk;
begin
ml:=mk.Create(true);
ml.FreeOnTerminate:=true;
ml.Resume;
end;
با تشكر

vcldeveloper
جمعه 11 دی 1388, 19:44 عصر
من به صورت زير Thread رو نوشتم و استفاده كردم آيا روش درسته؟
1- توی متد Execute از ShowMessage استفاده نکنید.
2- شی dele هیچ وقت در کد ساخته نشده.
3- DataList در کد Create شده، ولی جایی Free نشده، در نتیجه موجب نشت حافظه میشه.

MOJTABAATEFEH
جمعه 11 دی 1388, 22:26 عصر
1- توی متد Execute از ShowMessage استفاده نکنید.
2- شی dele هیچ وقت در کد ساخته نشده.
3- DataList در کد Create شده، ولی جایی Free نشده، در نتیجه موجب نشت حافظه میشه.

سلام
ممنون بابت جوابتون


شيء dele رو در Execute بسازم يا متد خودش كه InsertintoTable هست؟
Datalist رو در Execute قبل از Terminate قرار بدم؟

با تشكر

vcldeveloper
شنبه 12 دی 1388, 03:12 صبح
شيء dele رو در Execute بسازم يا متد خودش كه InsertintoTable هست؟
اگر فقط در InsertIntoTable ازش استفاده میشه، پس در همون متد آن را بسازید و آزاد کنید. وگرنه، در متد Create کلاس مربوطه آن را بسازید، و در متد Destroy آن را آزاد کنید.


Datalist رو در Execute قبل از Terminate قرار بدم؟
در پایان متد Execute نیازی به نوشتن Terminate نیست. هر زمان که اجرای Execute تمام بشه، یعنی Thread مربوطه Terminate شده.
DataList را غیر از Execute در یک متد دیگه هم استفاده کردید، پس یا در اول Execute آن را Create کنید، و در پایان Execute آن را Free کنید، یا اینکه آن را در متد Create کلاس مربوطه ایجاد کنید، و در متد Destroy کلاس آن را آزاد کنید.

MOJTABAATEFEH
شنبه 12 دی 1388, 13:35 عصر
اگر فقط در InsertIntoTable ازش استفاده میشه، پس در همون متد آن را بسازید و آزاد کنید. وگرنه، در متد Create کلاس مربوطه آن را بسازید، و در متد Destroy آن را آزاد کنید.


در پایان متد Execute نیازی به نوشتن Terminate نیست. هر زمان که اجرای Execute تمام بشه، یعنی Thread مربوطه Terminate شده.
DataList را غیر از Execute در یک متد دیگه هم استفاده کردید، پس یا در اول Execute آن را Create کنید، و در پایان Execute آن را Free کنید، یا اینکه آن را در متد Create کلاس مربوطه ایجاد کنید، و در متد Destroy کلاس آن را آزاد کنید.

به اين صورت نوشتم :




Function mk.insertintotable(stpt:TStringList):integer;
var
i:Integer;
dele:TADOQUERY;
begin
dele:=TADOQUERY.Create(self);
dele.SQL.Clear;
dele.SQL.Text:='دستور SQL';
dele.open;
dele.Insert;
for i := 0 to stpt.Count - 1 do begin
dele.Fields[i].AsVariant:= stpt[i];
end;
try
dele.post;
result:=1;
except
on E:Exception do
result:=-1;
end;
dele.FreeOnRelease;

end;
موقع كامپايل روي خط قرمز رنگ خطاي زير رو مي گيره (عدم همخواني TComponent و Thread ما) :



Incompatible types: TComponent and mk

vcldeveloper
شنبه 12 دی 1388, 21:26 عصر
موقع كامپايل روي خط قرمز رنگ خطاي زير رو مي گيره (عدم همخواني TComponent و Thread ما) :
برای اینه که به Create پارامتر Self را ارسال کردید. باید بجاش nil ارسال کنید.

در ضمن، برای آزاد کردن dele از Free استفاده کنید، نه FreeOnRelease.

نکته آخر هم اینکه برای ساختن و آزاد کردن اشیاء از بلوک try-finally استفاده کنید که تحت هر شرایطی کد dele.Free اجرا بشه.