PDA

View Full Version : حرفه ای: دلایل توصیه نشدن ارتباط با فرم در Thread از طریق تعریف Event



loo30fer
جمعه 01 آذر 1392, 20:20 عصر
با سلام خدمت تمامی دوستان
دوستان میشه بی زحمت توضیح بدین چرا مواقعی که ما در Thread نیاز به ارتباط با فرم داریم یا استفاده از Synchronize یا استفاده از SendMessage توصیه میشه در صورتی که با تعریف یک Event میشه اینکار رو انجام داد و مشکلی هم پیش نیاد مانند کدی که نوشتم :
با تشکر

unit Unit1;

interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IdComponent,
IdHTTP,
IdTCPConnection,
IdTCPClient,
IdBaseComponent;

type
TForm1 = class(TForm)
btn1: TButton;
Label1: TLabel;
btn2: TButton;
btn3: TButton;
procedure btn1Click(Sender: TObject);
procedure btn3Click(Sender: TObject);
procedure btn2Click(Sender: TObject);
private
procedure OnIdHTTPWork(ASender: TObject; AWorkCount: Int64);
{ Private declarations }
public
{ Public declarations }
end;

type
TOnWorkEvent = procedure(Sender: TObject; AWorkCount: int64) of object;
TDownloadThread = class (TThread)
private
FIdHTTP : TIdHTTP;
FURLHost : String;
FWork : TOnWorkEvent;
procedure Initialize;
//EVENT IDHTTP
procedure IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode;
AWorkCount: Int64);
public
constructor Create(AURL: String);
procedure DownloadFile;

property OnWork : TOnWorkEvent read FWork write FWork;

protected
procedure Execute; override;
destructor Destory;
end;

var
Form1: TForm1;
th : TDownloadThread;

implementation

{$R *.dfm}

{------------------------------------ Thread ----------------------------------}

procedure TDownloadThread.IdHTTPWork(ASender: TObject; AWorkMode: TWorkMode;
AWorkCount: Int64);
begin
OnWork(self,AWorkCount);
end;

constructor TDownloadThread.Create(AURL: String);
begin
inherited Create(True);
FreeOnTerminate := False;
FURLHost := AURL;
end;

procedure TDownloadThread.Initialize;
begin
FIdHTTP:= TIdHTTP.Create(nil);
with FIdHTTP do
begin
Request.Host := '';
Request.Username := '';
Request.Password := '';
OnWork := IdHTTPWork;
end;
end;

procedure TDownloadThread.DownloadFile;
begin
FIdHTTP.Get(FURLHost);
end;

procedure TDownloadThread.Execute;
begin
Initialize;
DownloadFile;
end;

destructor TDownloadThread.Destory;
var
i : integer;
begin
FreeAndNil(FIdHTTP);
inherited;
end;

{---------------------------------- End Thread --------------------------------}

procedure TForm1.btn1Click(Sender: TObject);
var
AUrl : String;
begin
AUrl := 'http://dl2.asandownload.com/mobile/android/application/Viber.v4.0.0.1707_www.AsanDownload.com.apk';
th := TDownloadThread.Create(AUrl);
th.OnWork := OnIdHTTPWork;
th.Start;
end;

procedure TForm1.btn2Click(Sender: TObject);
begin
th.Destory;
end;

procedure TForm1.btn3Click(Sender: TObject);
begin
ShowMessage('hiiiiii');
end;

procedure TForm1.OnIdHTTPWork(ASender: TObject; AWorkCount: Int64);
begin
Label1.Caption := IntToStr(AWorkCount);
end;

end.

Felony
شنبه 02 آذر 1392, 09:36 صبح
چون این کاری که شما کردی با صدا زدن مستقیم اون Event تفاوتی نداره ، فقط مرتبش کردی !

این ترد رو در نظر بگیر :


TMyEvent = procedure(Sender: TObject; Num: Integer) of object;

TTest = class(TThread)
strict private
FMyEvent: TMyEvent;
public
procedure Execute; override;
property Event: TMyEvent read FMyEvent write FMyEvent;
end;

این هم متد Execute ش :


procedure TTest.Execute;
begin
FMyEvent(Self, 0);
end;

این هم یک فانکشن در فرم برنامت :


procedure TForm1.CallMe(Sender: TObject; Num: Integer);
var
MyAddress: PInteger;
begin
MyAddress := Addr(TForm1.CallMe);
ShowMessage(Format('My address is [%x] and my value is [%d]', [MyAddress^, Num]));
end;

حالا 2 تا دکمه بزار روی فرمت ، کد یکی این :


CallMe(Self, 0);

کد یکی این :


var
TestThread : TTest;
begin
TestThread:= TTest.Create(True);
TestThread.FreeOnTerminate := True;
TestThread.Event := CallMe;
TestThread.Start;
end;

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

شما اگر مستقیم هم اون ایونت فرمت رو تو ترد صدا بزنی کار میکنه ، برای تست کد متد Execute رو اینطور بنویس :


procedure TTest.Execute;
begin
FMyEvent(Self, 0);
Form1.CallMe(Self, 0);
end;

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

روز خوش .

یوسف زالی
شنبه 02 آذر 1392, 09:48 صبح
در تکمیل فرمایشات دوست عزیز:
شما Label1.Caption رو در یک حلقه یا تایمر روی خود فرم دستکاری کن، فکر می کنی وقتی هر دو ترد می رسن به تغییر Label1.Caption چه اتفاقی می افته؟
یکی باید wait بشه، غیر از اینه؟
دلیل این که در مثال شما اتفاقی نمی افته اینه که احتمال برخورد ترد اصلی با ترد شما به دلیل کدهای خیلی کم، کم هست ولی منتفی نیست.
سعی کردم ساده توضیح بدم. مسایل فنی تری داره که با تجربه بدست میاد. خیلی هاش رو خود من هم بلد نیستم.

loo30fer
شنبه 02 آذر 1392, 15:10 عصر
چون این کاری که شما کردی با صدا زدن مستقیم اون Event تفاوتی نداره ، فقط مرتبش کردی !

این ترد رو در نظر بگیر :


TMyEvent = procedure(Sender: TObject; Num: Integer) of object;

TTest = class(TThread)
strict private
FMyEvent: TMyEvent;
public
procedure Execute; override;
property Event: TMyEvent read FMyEvent write FMyEvent;
end;

این هم متد Execute ش :


procedure TTest.Execute;
begin
FMyEvent(Self, 0);
end;

این هم یک فانکشن در فرم برنامت :


procedure TForm1.CallMe(Sender: TObject; Num: Integer);
var
MyAddress: PInteger;
begin
MyAddress := Addr(TForm1.CallMe);
ShowMessage(Format('My address is [%x] and my value is [%d]', [MyAddress^, Num]));
end;

حالا 2 تا دکمه بزار روی فرمت ، کد یکی این :


CallMe(Self, 0);

کد یکی این :


var
TestThread : TTest;
begin
TestThread:= TTest.Create(True);
TestThread.FreeOnTerminate := True;
TestThread.Event := CallMe;
TestThread.Start;
end;

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

شما اگر مستقیم هم اون ایونت فرمت رو تو ترد صدا بزنی کار میکنه ، برای تست کد متد Execute رو اینطور بنویس :


procedure TTest.Execute;
begin
FMyEvent(Self, 0);
Form1.CallMe(Self, 0);
end;

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

روز خوش .

توصیف جالبی بود , راستش بنده تصورم از ایجاد مشکل تنها اختلال در ترسیم اجزای بصری و امکان رخداد خطا و ایجاد مشکل حتما در همان دفعه اول و لحظه فعالیت Thread بود که با توضیحات شما و جناب You-See (http://barnamenevis.org/member.php?70247-You-See) یه چیزهایی دستگیرم شد.
خوب پس در صورتی که بیام این رویدادها رو به یک کلاس از نوع TObject که واسط بین Thread و فرم برنامست پاس بدم نمیتونه مشکل ساز باشه درسته؟ البته با توجه به اینکه کلاس واسط تغییراتی در مقدار رویدادها داده و توسط رویدادها یا SendMessage این مقادیر رو به فرم میفرسته.
بابت توجهتون بسيار سپاسگزارم .
جناب You-See (http://barnamenevis.org/member.php?70247-You-See) از لطف شما هم ممنونم.

Mask
شنبه 02 آذر 1392, 15:54 عصر
در ادامه صحبتهای 2 دوست عزیزم :
حتی مورد داشتیم :چشمک:
پس از برخورد دو ترد با هم یکیشون terminate شده.
به عقیده من ، برای جلوگیری از این امور مشکلات ، و چون ساختار تردها و روش Sync شون رو به طور صحیح و کامل نمیدونیم و اگر هم بدونیم ، برای پیاده سازیشون باید وقت زیادی صرف بشه ، بار ترتیب و تنظیم کارهارو به عهده دلفی و ویندوز بندازیم.
با اینکه روشهای کریتیکال سکشن موجود رو میتونیم به کار بگبربم ، اما پیشنهاد من همیشه برای چنین اموری، استفاده و مسولیت دادن به کریتیکالهای خود تردمنیجر دلفی هست.
موفق باشید.