View Full Version : مشكل با Thread ها
  
MOJTABAATEFEH
چهارشنبه 09 دی 1388, 15:04 عصر
سلام دوستان عزيز من جستجو كردم اما چيزي راجع به موضوع مورد نظرم پيدا نكردم
من از Thread جاهاي مختلف استفاده كردم اما ايندفعه به يك مشكل برخورد كردم اينكه تا الان از پروسيجر در ترد استفاده مي كردم بدون پارامتر حالا چطور مي تونم از تابع و پروسيجر (با پارامتر) استفاده كنم  Synchronize براي تابع يا پروسيجر با پارامتر كار نمي كنه چكار بايد كرد؟
با تشكر
vcldeveloper
چهارشنبه 09 دی 1388, 18: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, 01: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, 01:41 صبح
خب، این که کار خاصی نداره، شما می تونید همه این متغیرهای عمومی را بصورت فیلد کلاس برای کلاس Threadتان تعریف کنید، توابع TravelChildren و InsertIntoTable را هم به عنوان متدهای همان کلاس تعریف کنید. کد مربوط به Button2Click را هم در متد Execute اون Thread بنویسید، با این تفاوت که کدهای مربوط به تغییر رابط کاربر (مثل کدهای نوشتن در Memo) را از آن خارج کنید.
در نهایت کدهای مربوط به تغییر در رابط کاربر را می تونید در یک یا چند متد بنویسید، و آنها را با استفاده از Synchronize یا Queue فراخوانی کنید.
البته در این مورد خاص، چون دارید از اشیاء COM برای کار با XML استفاده می کنید، باید در ابتدای متد Execute تابع CoInitialize را فراخوانی کنید، و در پایان آن تابع CoUninitialize. این دو تابع در یونیت ActiveX قرار دارند. درباره این دو تابع قبلا توضیح داده شده.
MOJTABAATEFEH
جمعه 11 دی 1388, 11:19 صبح
خب، این که کار خاصی نداره، شما می تونید همه این متغیرهای عمومی را بصورت فیلد کلاس برای کلاس Threadتان تعریف کنید، توابع TravelChildren و InsertIntoTable را هم به عنوان متدهای همان کلاس تعریف کنید. کد مربوط به Button2Click را هم در متد Execute اون Thread بنویسید، با این تفاوت که کدهای مربوط به تغییر رابط کاربر (مثل کدهای نوشتن در Memo) را از آن خارج کنید.
در نهایت کدهای مربوط به تغییر در رابط کاربر را می تونید در یک یا چند متد بنویسید، و آنها را با استفاده از Synchronize یا Queue فراخوانی کنید.
البته در این مورد خاص، چون دارید از اشیاء COM برای کار با XML استفاده می کنید، باید در ابتدای متد Execute تابع CoInitialize را فراخوانی کنید، و در پایان آن تابع CoUninitialize. این دو تابع در یونیت ActiveX قرار دارند. درباره این دو تابع قبلا توضیح داده شده.
جناب كشاورز ممنون بابت جوابتون من CoInitialize را جستجو كردم فقط دو مورد پيدا شد كه يكي همين بحث و ديگري هم مشابه اينجا از كلمه CoInitialize استفاده شده بود و توضيحي نبود لطفا راجع به استفاده از CoInitialize و CounInitialize كمي توضيح دهيد
با تشكر
vcldeveloper
جمعه 11 دی 1388, 12:39 عصر
لطفا راجع به استفاده از CoInitialize و CounInitialize كمي توضيح دهيد
درباره چگونگی استفاده شون و چرایی استفاده شون در پست قبل توضیح دادم.
جزئییاتش در تاپیک فعلی به کار شما نمیاد؛ خلاصه اش این هست که اشیاء COM باید برای هر Thread مقداردهی اولیه بشند. برای Thread اصلی این کار توسط Application.Initialize بطور خودکار انجام میشه. Threadهای دیگه باید خودشون این کار را انجام بدند.
MOJTABAATEFEH
جمعه 11 دی 1388, 13:22 عصر
درباره چگونگی استفاده شون و چرایی استفاده شون در پست قبل توضیح دادم.
جزئییاتش در تاپیک فعلی به کار شما نمیاد؛ خلاصه اش این هست که اشیاء COM باید برای هر Thread مقداردهی اولیه بشند. برای Thread اصلی این کار توسط Application.Initialize بطور خودکار انجام میشه. Threadهای دیگه باید خودشون این کار را انجام بدند.
CoInitialize داراي پارامتر هست با اون بايد چكار كرد؟
لطفا يك مثال بزنيد
با تشكر
vcldeveloper
جمعه 11 دی 1388, 13:25 عصر
CoInitialize داراي پارامتر هست با اون بايد چكار كرد؟
nil
-------------
MOJTABAATEFEH
جمعه 11 دی 1388, 19: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, 20:44 عصر
من به صورت زير Thread رو نوشتم و استفاده كردم آيا روش درسته؟
1- توی متد Execute از ShowMessage استفاده نکنید.
2- شی dele هیچ وقت در کد ساخته نشده.
3- DataList در کد Create شده، ولی جایی Free نشده، در نتیجه موجب نشت حافظه میشه.
MOJTABAATEFEH
جمعه 11 دی 1388, 23:26 عصر
1- توی متد Execute از ShowMessage استفاده نکنید.
2- شی dele هیچ وقت در کد ساخته نشده.
3- DataList در کد Create شده، ولی جایی Free نشده، در نتیجه موجب نشت حافظه میشه.
سلام
ممنون بابت جوابتون
 شيء dele رو در Execute بسازم يا متد خودش كه InsertintoTable هست؟
 Datalist رو در Execute قبل از Terminate قرار بدم؟
با تشكر
vcldeveloper
شنبه 12 دی 1388, 04: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, 14: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, 22:26 عصر
موقع كامپايل روي خط قرمز رنگ خطاي زير رو مي گيره (عدم همخواني TComponent و Thread ما) :
برای اینه که به Create پارامتر Self را ارسال کردید. باید بجاش nil ارسال کنید.
در ضمن، برای آزاد کردن dele از Free استفاده کنید، نه FreeOnRelease.
نکته آخر هم اینکه برای ساختن و آزاد کردن اشیاء از بلوک try-finally استفاده کنید که تحت هر شرایطی کد dele.Free اجرا بشه.
 
vBulletin® v4.2.5, Copyright ©2000-1404, Jelsoft Enterprises Ltd.