PDA

View Full Version : سوال: راهنمایی کنید: جلوگیری از گیر کردن برنامه، هنگام دانلود یک فایل



ahmad.khaliq
شنبه 21 اسفند 1389, 02:13 صبح
سلام عزیزان
شاید سوالی که میپرسم خیلی مبتدی باشه.

من دارم یک برنامه مینویسم که به دلایلی باید هر 5 ثانیه وصل بشه به سایت من و یک نوشته رو از روی سایت بخونه، بعد در ازای فرمانی که از سرور گرفته کاری رو انجام بده.

من یک تایمر ایجاد کردم که هر 5 ثانیه کد زیر رو اجرا میکنه:


function Get(): string;
var
Params: TStringList;
IHTTP: TIDHTTP;
begin
IHTTP := TIDHTTP.Create(nil);
Params := TStringList.Create;
Params.Add('request=somthing' );
try
Result := IHTTP.Post('http://domain.com/get.php',Params);
finally
FreeAndNil(IHTTP);
FreeAndNil(Params);
end;
end;

نوشته ای که از طریق سایت دریافت میشه خیلی کوتاه و کم حجم هست اما متاسفانه هنگامی که نرم افزار میخواد همین حجم کم رو دریافت کنه، یه کوچولو درگیر میشه و برای یه لحظه گیر میکنه


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


چطور میتونم این کد رو طوری اجرا کنم که جدا از برنامه من باشه. یعنی یه جوری که وقتی میخواد وصل بشه به سرور ، اگر برای چند لحظه گیر هم کرد، برنامه اصلی گیر نکنه.


لطفا راهماییم کنید! خیلی مهمه.

ضمنا! لطفا کامل توضیح بدید چون من هنوز مبتدی هستم.

ممنون :قلب:

Felony
شنبه 21 اسفند 1389, 06:21 صبح
باید قسمت دانلود رو تو یک Thread جدا پیاده کنید ، بارها در مورد Thread بحث شده ، همین عبارت رو جست و جو کنید .

ahmad.khaliq
شنبه 21 اسفند 1389, 10:29 صبح
باید قسمت دانلود رو تو یک Thread جدا پیاده کنید ، بارها در مورد Thread بحث شده ، همین عبارت رو جست و جو کنید .

ممنون
ولی با thread هم نشد. بازم گیر میکنه. قطعا یه جایی اشتباه کردم

این کد منه

یک یونیت دارم که thread داخلش قرار داره:




type
TGetText = class(TThread)
public
function Get():string;
private
address: string;

protected
procedure Execute; override;

end;

implementation
uses main;


function TGetText.Get(): string;
var
Params: TStringList;
IHTTP: TIDHTTP;
begin
IHTTP := TIDHTTP.Create(nil);
Params := TStringList.Create;
Params.Add('request=somthing' );
try
Result := IHTTP.Post('http://domain.com/get.php',Params);
finally
FreeAndNil(IHTTP);
FreeAndNil(Params);
end;
end;


procedure TGetText.Execute;
begin
//Synchronize(Get);
if Terminated then
Exit;
end;





حالا داخل پروسیجر تایمر، این کد رو نوشتم:





procedure Tmainform.input_checkTimer(Sender: TObject);
var
Text_string, Web_String, Temp_WEb_String: string;
temp: string;
i: integer;
GetITEGText : TGetText;
begin
GetITEGText:= TGetText.Create(True);
GetITEGText.FreeOnTerminate := True;
GetITEGText.Resume;
Web_String := GetITEGText.Get();
// do somthing
if GetITEGText.ExternalThread = false then
GetITEGText.Terminate;





کدهای بالا رو از روی یک مثال نوشتم. حالا نمیدونم کجاش اشتباه کردم.

Felony
شنبه 21 اسفند 1389, 11:00 صبح
استفاده از Synchronize باعث میشه تا تابع فراخوان شده در Context ترد اصلی برنامه ( Main Thread ) اجرا بشه و زمانی کاربرد داره که شما بخواین از داخل تردتون به اشیاء فرم دسترسی داشته باشید ( مثلا روند یک ProgressBar رو تغییر بدید یا ... ) که در این صورت برای همزمان سازی تردتون با Main Thread میتونید از Synchronize استفاده کنید ، پس ادامه اون روند فریز شدن طبیعی هست ، در ضمن روند اجرای ترد رو باید به روال Execute ترد بسپارید نه اینکه مستقیما تابع Get رو صدا بزنید ، اینطوری عملا هیچ استفاده ای از Thread نکردی !

کلا تردتون پر از اشکال هست ، تا بعد از ظهر سرم شلوغه و نمیتونم کمکت کنم ، اگر تا اون موقع به جایی نرسیدی کد رو برات اصلاح میکنم .

Mahmood_M
شنبه 21 اسفند 1389, 13:06 عصر
برای استفاده از Thread باید درخواستهایی که از ترد و Object های متعلق به اون دارید رو درون Execute مربوط به Thread اجرا کنید ، وقتی درون تردتون چیزی رو Create میکنید ، اون شیء متعلق به ترد هست ، چیزی که باعث به اصطلاح هنگ کردن برنامه میشه درخواست استفاده از آبجکتهای یک ترد از طرف ترد دیگه بدون همزمان سازیه ، در نمونه ی بالا می تونید مقدار String خروجی رو در یک متغیر در داخل خود ترد قرار بدید و از اون بخونید :
type
TGetText = class(TThread)
public
Rst : String;
procedure Get;
private
Params : TStringList;
IHTTP : TIDHTTP;
Address: string;
protected
procedure Execute; override;

end;
بهتره که آبجکت ها درون بدنه ترد تعریف بشند تا درون Procedure

procedure TGetText.Get;
begin
IHTTP := TIDHTTP.Create();
Params := TStringList.Create;
Params.Add('request = somthing');
try
Rst := IHTTP.Post(Address, Params);
finally
FreeAndNil(IHTTP);
FreeAndNil(Params);
end;
end;


procedure TGetText.Execute;
begin
try
Get;
finally
Terminate;
end;
end;

البته بهتره که Object ها در رویداد OnCreate ترد Create و در رویداد OnDestroyed ترد هم Free بشن

var
Text_string, Web_String, Temp_WEb_String: string;
temp: string;
i: integer;
GetITEGText : TGetText;
begin
GetITEGText := TGetText.Create(True);
GetITEGText.FreeOnTerminate := True;
GetITEGText.Resume;
if GetITEGText.Rst <> '' then
Web_String := GetITEGText.Rst;
end;

این تغییرات مشکل هنگ کردن برنامه رو حل میکنن ولی اگر قصد استفاده بهتر از ترد رو دارید بهتره کمی در موردش تحقیق کنید ، در همین سایت هم قبلا بحث های مفیدی شده ، موفق باشید ...

ahmad.khaliq
شنبه 21 اسفند 1389, 15:25 عصر
ممنون

اما یه جای کار میلنگه!!!

وقتی کد زیر اجرا میشه:


var
Text_string, Web_String, Temp_WEb_String: string;
temp: string;
i: integer;
GetITEGText : TGetText;
begin
GetITEGText := TGetText.Create(True);
GetITEGText.FreeOnTerminate := True;
GetITEGText.Resume;
if GetITEGText.Rst <> '' then
Web_String := GetITEGText.Rst;
end;



وقتی از طریق main مقدار rst رو دریافت میکنم خالی هستش، در حالی که وقتی در داخل ترد تست میکنم، rst مقدار داره! یعنی GetITEGText.Rst یک مقدار خالی رو بهم میده.
مگه ممکنه؟


ممنون میشم راهنمایی کنید.

Felony
یک شنبه 22 اسفند 1389, 15:57 عصر
ممنون

اما یه جای کار میلنگه!!!

وقتی کد زیر اجرا میشه:


var
Text_string, Web_String, Temp_WEb_String: string;
temp: string;
i: integer;
GetITEGText : TGetText;
begin
GetITEGText := TGetText.Create(True);
GetITEGText.FreeOnTerminate := True;
GetITEGText.Resume;
if GetITEGText.Rst <> '' then
Web_String := GetITEGText.Rst;
end;



وقتی از طریق main مقدار rst رو دریافت میکنم خالی هستش، در حالی که وقتی در داخل ترد تست میکنم، rst مقدار داره! یعنی GetITEGText.Rst یک مقدار خالی رو بهم میده.
مگه ممکنه؟


ممنون میشم راهنمایی کنید.

اون کد مشکل داره ، متغییر Rst رو به صورت سراسری تعریف کنید .

vcldeveloper
یک شنبه 22 اسفند 1389, 19:25 عصر
بهتره که آبجکت ها درون بدنه ترد تعریف بشند تا درون Procedureمزیت خاصی نداره، بخصوص تنها استفاده کننده اون اشیاء همون متد Get هست، نه مابقی متدهای اون کلاس. در ضمن، Thread ایی که اجرای متد Execute اش به اتمام برسه، به طور خودکار Terminate میشه، نیازی به فراخوانی Terminate در انتهای متد Execute نیست.



وقتی از طریق main مقدار rst رو دریافت میکنم خالی هستش، در حالی که وقتی در داخل ترد تست میکنم، rst مقدار داره! یعنی GetITEGText.Rst یک مقدار خالی رو بهم میده.من قبلا هم گفتم، قبل از استفاده از Thread ها باید خوب درباره شان و مفهوم شان مطالعه کنید، وگرنه استفاده از Thread بیشتر باعث ایجاد مشکل میشه تا رفع مشکل. اگر کل مشکل شما اینه که برنامه برای چند لحظه در هنگام دریافت متن از اینترنت حالت قفل شده پیدا میکنه، بهتره به جای استفاده از Thread، یک کامپوننت IdAntiFreeze روی فرم تان بیاندازید، تا دیگه برنامه تان هنگام کار با کامپوننت های Indy حالت قفل شده پیدا نکنه.

اگر خواستید با Thread کار کنید، باید بدونید که کد نوشته شده در اون Thread با کد اصلی شما به طور موازی اجرا میشه، یعنی وقتی شما Thread رو ایجاد میکنید، Thread اصلی برنامه شما منتظر اتمام اجرای Thread ساخته شده نمیشه. هر دو با هم اجرا میشند. پس وقتی شما مقدار خروجی را چک می کنید، معلوم نیست که آیا کار Thread دوم تمام شده یا نه. اگر تمام شده باشه، مقدار خروجی رو میگیرید، اگر هم تمام نشده باشه، خروجی ندارید.

برای کار شما، ساده ترین حالت استفاده از Thread این هست که از همون متد Get خودتون در داخل متد Execute استفاده کنید، فقط یک خصوصیت برای نگهداری مقدار خروجی Thread به صورت read-only در داخل کلاس Thread تعریف کنید، و خروجی Get را داخل آن بریزید. برای دریافت این مقدار از Thread، می تونید از رویداد OnTerminate اون Thread استفاده کنید. وقتی Thread اجراش به پایان رسید، اون رویداد فراخوانی میشه، و شما می تونید مقدار خصوصیت مربوطه را در Thread اصلی دریافت کنید. کدتون میشه یک چیزی شبیه به این:

TGetText = class(TThread)
private
FResult: string;
protected
procedure Execute; override;
function Get: string;
public
property Result : string read FResult;
end;

procedure TGetText.Execute;
begin
FreeOnTerminate := True;
FResult := Get;
end;

function TGetText.Get: string;
begin
/// Your get() method code
end;

کد Thread اصلی:

TForm1 = class(TForm)
procedure Timer1OnTimer(Sender: TObject);
private
FGetText : TGetText;
procedure GetTextTerminate(Sender: TObject);
public
end;

procedure TForm1.Timer1OnTimer(Sender: TObject);
begin
if Assigned(FGetText) then
begin
FGetText.Terminate;
FGetText.WaitFor;
FreeAndNil(FGetText);
end;

FGetText := TGetText.Create(True);
FGetText.OnTerminate := GetTextTerminate;
FGetText.Resume;
end;

procedure TForm1.GetTextTerminate(Sender: TObject);
begin
ShowMessage(FGetText.Result);
FGetText := nil;
end;

لطفا همینطوری Copy\Paste اش نکنید، درباره اش مطالعه کنید.

موفق باشید