PDA

View Full Version : سوال: گرفتن خروجی تابع از روال ترد



Mask
یک شنبه 24 اردیبهشت 1391, 19:14 عصر
با سلام.
تابعی نوشته ام که در آن تردی ساخته شده و عملیاتی انجام داده میشود.
مشکلم اینجاست که میخواهم خروجی ترد همان result تابعم باشد.
نمونه را ملاحظه بفرمایید:(این نمونه فقط مثال است)


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

type
TSumThread=class(TThread)
private
Num1T: string;
Num2T: string;
Num3T:Integer;
protected
procedure Execute; override;
procedure error;
procedure Sum;
public
constructor Create(Num1P: string; Num2P: string);
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

constructor TSumThread.Create(Num1P, Num2P: string);
begin
inherited Create(True);
Num1T := Num1P;
Num2T := Num2P;
FreeOnTerminate := True;
Resume;
end;

procedure TSumThread.error;
begin
Num3T:=0;
ShowMessage(IntToStr(Num3T));
end;

procedure TSumThread.Sum;
begin
ShowMessage(IntToStr(Num3T));
end;

procedure TSumThread.Execute;
begin
inherited;
try
Num3T:=StrToInt(Num1T)+StrToInt(Num2T);
Synchronize(sum);
except
Synchronize(error);
end;
end;

function FSum(Num1P,Num2P: string):Boolean;
var
PSumThread: TSumThread;
begin
try
PSumThread := TSumThread.Create(Num1P,Num2P);
Result:=True;
except
Result:=False;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if FSum(Edit1.Text,Edit2.Text) then
ShowMessage('yes')
else
ShowMessage('no');
end;

end.

من میخام خروجی تابعم مقدار خروجی Execute تردهام باشه.؟
یعنی اینکه در ترد عملیات با موفقیت انجام شده مقدار True به تابع برگرده و اگه مشکلی پیش اومده باشه(مثلا یه حرف رو نتونسته باشه به عدد تبدیل کنه) مقدار Falseبرگرده.
چطوری باید این کار رو بکنم.

یوسف زالی
سه شنبه 26 اردیبهشت 1391, 20:02 عصر
سلام.
این نظر غیر کارشناسانه هست:
می تونی یک متغیر دیگه به create پاس بدی و اون رو تغییر بدی.
بعد مطمئن شی که اون پارامتر به یکی از سه حالت (انجام موفق - انجام ناموفق - تایم اوت) رفته.

Felony
چهارشنبه 27 اردیبهشت 1391, 06:17 صبح
میشه همچین کدی نوشت :

type
TSumThread = class(TThread)
private
FNum1: string;
FNum2: string;
FResultValue: Boolean;
protected
procedure Execute; override;
public
constructor Create(Num1P: string; Num2P: string);
property ResultValue: Boolean read FResultValue write FResultValue;
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

constructor TSumThread.Create(Num1P, Num2P: string);
begin
inherited Create(True);
FNum1 := Num1P;
FNum2 := Num2P;
Resume;
end;

procedure TSumThread.Execute;
begin
inherited;
ResultValue := (FNum1 = FNum2);
end;

function FSum(Num1P, Num2P: string): Boolean;
var
PSumThread: TSumThread;
begin
PSumThread := TSumThread.Create(Num1P, Num2P);
try
PSumThread.WaitFor;
Result := PSumThread.ResultValue;
finally
PSumThread.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if FSum(Edit1.Text, Edit2.Text) then
ShowMessage('yes')
else
ShowMessage('no');
end;

Felony
چهارشنبه 27 اردیبهشت 1391, 11:55 صبح
من اون کد رو برای بررسی تساوی دو عدد تغییر دادم ، یعنی بررسی میکنه که دو عدد پاس داده شده به تابع یکی باشن ، تو این خط :
ResultValue := (FNum1 = FNum2);

خودت هر جور میخوای تغییرش بده .

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

Mask
چهارشنبه 27 اردیبهشت 1391, 14:27 عصر
ممنون. اما الان 2 تا مشکل به وجود اومد.
برنامه قفل میشه و اینکه ترد ها با هم ساخته نمیشند.
تا زمانی که یه ترد خروجیشو نده ترد بعدی ساخته نمیشه.
در مورد critical section هم تحقیق کردم و روش نوشتنش رو فهمیدم. فقط موندم اینکه کجای این کد باید از critical section استفاده بشه.
من وقتی ترد رو اینجوری میسازم :

if FSum(Edit1.Text, Edit2.Text) then
ShowMessage('yes')
else
ShowMessage('no');
خروجی تابع رو چطوری از متغیر قفل شده critical section دریافت کنم.

vcldeveloper
جمعه 29 اردیبهشت 1391, 13:09 عصر
هر چند به نظرم همچین کاری (چک کردن یک مقدار ساده در Thread، و منتظر موندن برای همون مقدار ساده)، کار زایدی هست (شاید مدت زمان لازم برای ساخت یک Thread جدید از زمان لازم برای چک کردن همون مقدار در Thread اصلی بیشتر هم بشه!!)، ولی برای روشن شدن روال کلی محاسبه در یک Thread و دریافت خروجی به صورت غیر همزمان در یک Thread دیگه، کد زیر رو قرار دادم:

uses
OtlParallel;

function IsSumDone(const Num1T, Num2T: string): Boolean;
var
Future : IOmniFuture<Boolean>;
begin
Future := TOmniFuture<Boolean>.Create(function: Boolean
var
V1, V2 : Integer;
begin
Result := TryStrToInt(Num1T, V1) and TryStrToInt(Num2T, V2);
end);
Result := Future.Value;
end;

البته این کد از کتابخانه OmniThread استفاده کرده، که باید دانلود و نصبش کنید. بهتر از اینه که برای همچین اموری مستقیما با Thread درگیر بشید.

Mask
شنبه 30 اردیبهشت 1391, 13:59 عصر
در اولین پستم عرض کردم که این یه مثاله.

ظرم همچین کاری (چک کردن یک مقدار ساده در Thread، و منتظر موندن برای همون مقدار ساده)، کار زایدی هست (شاید مدت زمان لازم برای ساخت یک Thread جدید از زمان لازم برای چک کردن همون مقدار در Thread اصلی بیشتر هم بشه!!)
این کدها رو در یه برنامه شبکه دارم استفاده میکنم. که اونجا زمان انجام عملیات به قطع ،خیلی خیلی بیشتر از زمان ساخت یه ترد طول میکشه.
در کدی که زحمت کشیدی و قرار دادید خروجی عملیات واضح و روشنه که از :

TryStrToInt(Num1T, V1) and TryStrToInt(Num2T, V2);
دریافت میشه.
بزرگترین مشکل من اینه که این خروجی رو چطوری از تردم بگیرم و به این تابع پاس بدهم.؟
البته اگه فکر میکنید که جهت روشن شدن مطلب نیاز به وجود کد برنامه اصلی هست بفرمایید تا کد اصلی رو اینجا قرار بدهم.
ممنون از توجهتون.

vcldeveloper
دوشنبه 01 خرداد 1391, 20:26 عصر
بزرگترین مشکل من اینه که این خروجی رو چطوری از تردم بگیرم و به این تابع پاس بدهم.؟
روال کار Future ها به این صورت هست که یک عملیات را در پس زمینه شروع می کنند و نتیجه عملیات را در خروجی خودشان (خصوصیت Value) نگه داری می کنند، بدون اینکه مانع فعالیت Thread فراخوان بشند. هر زمان که Thread ای به نتیجه نیاز داشت، به خصوصیت Value دسترسی پیدا میکنه. اگر در زمان دسترسی به خصوصیت Value، نتیجه عملیات آماده بود، مقدار Value برگشت داده میشه. اگر نتیجه عملیات هنوز آماده نشده بود، اون Thread به طور خودکار منتظر Value میشه. هر وقت که Value آماده شد، مقدارش رو میگیره و به کار خودش ادامه میده.

Mask
دوشنبه 01 خرداد 1391, 21:02 عصر
ممنونم از استاد عزیزم.
اما با راهنمایی هایی که فرمودید بازم برنامه نمیتونه جواب درست رو نمایش بده.
کد اصلیم رو قرار میدم. لطفا مطلاعه بفرمایید:

unit Unit1;

interface

uses
Windows, Classes, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
IdRawBase,IdRawClient, IdIcmpClient, Messages, SysUtils, Variants, Graphics,
Controls, Forms, Dialogs, StdCtrls, ExtCtrls, OtlParallel;

type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Memo1: TMemo;
Memo2: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

type
TPSThread=class(TThread)
private
FIcmpClient: TIdIcmpClient;
FIdTCPClient: TIdTCPClient;
FActiveServer: Boolean;
HostT: string;
PortT: Integer;
SeqT: Integer;
FResultValue: Boolean;
protected
procedure Execute; override;
procedure IsInactiveServer;
procedure SentSuccessfullyToServer;
procedure SendMessageFails;
public
constructor Create(HostP: string; PortP: Integer; var SeqP: Integer);
property ResultValue: Boolean read FResultValue write FResultValue;
end;

var
Form1: TForm1;
Seq: Integer=0;

implementation

{$R *.dfm}
constructor TPSThread.Create(HostP: string; PortP: Integer; var SeqP: Integer);
begin
inherited Create(True);
FIcmpClient := nil;
FIdTCPClient := nil;
Inc(SeqP);
HostT := HostP;
PortT := PortP;
SeqT := SeqP;
FreeOnTerminate := True;
Resume;
end;

procedure TPSThread.IsInactiveServer;
begin
Form1.Memo1.Lines.Add(' Server '+HostT+' Is inactive ');
ResultValue:=false;
end;

procedure TPSThread.SentSuccessfullyToServer;
begin
Form1.Memo1.Lines.Add(' Message was sent successfully to server '+HostT);
ResultValue:=true;
end;

procedure TPSThread.SendMessageFails;
begin
Form1.Memo1.Lines.Add(' Send message to the server fails '+HostT);
ResultValue:=false;
end;

procedure TPSThread.Execute;
begin
inherited;

FIcmpClient := TIdIcmpClient.Create;
try
with FIcmpClient do
begin
ReceiveTimeout := 5000;
PacketSize := 28;
Host := HostT;
try
Ping(HostT, SeqT);
FActiveServer := (ReplyStatus.BytesReceived > 0);
except
FActiveServer := False;
end;
end;

if not FActiveServer then
begin
Synchronize(IsInactiveServer);
end;

if FActiveServer then
begin
FIdTCPClient := TIdTCPClient.Create;
with FIdTCPClient do
begin
Host := HostT;
Port := PortT;
UseNagle := False;
try
Connect;
IOHandler.WriteLn('Hello');
Synchronize(SentSuccessfullyToServer);
except
Synchronize(SendMessageFails);
end;
end;
end;

finally
if Assigned(FIcmpClient) then
FIcmpClient.Free;
if Assigned(FIdTCPClient) then
begin
if FIdTCPClient.Connected then
FIdTCPClient.Disconnect;
FIdTCPClient.Free;
end;
end;

end;

function PS_System(HostP: string; PortP: Integer): Boolean;
var
Future : IOmniFuture<Boolean>;
begin
Future := TOmniFuture<Boolean>.Create(function: Boolean
var
PSThread: TPSThread;
V1, V2 : Integer;
begin
PSThread := TPSThread.Create(HostP, PortP, Seq);
Result :=PSThread.ResultValue;
end);
Result := Future.Value;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Clear;
Memo2.Clear;
if PS_System(Edit1.Text,4321)then
Memo2.Lines.Add('yes')
else
Memo2.Lines.Add('no');
if PS_System(Edit2.Text,4321)then
Memo2.Lines.Add('yes')
else
Memo2.Lines.Add('no');
if PS_System(Edit3.Text,4321) then
Memo2.Lines.Add('yes')
else
Memo2.Lines.Add('no');
if PS_System(Edit4.Text,4321)then
Memo2.Lines.Add('yes')
else
Memo2.Lines.Add('no');
if PS_System(Edit5.Text,4321)then
Memo2.Lines.Add('yes')
else
Memo2.Lines.Add('no');
end;

end.
و خود پروژه رو هم از ضمیمه میتونید دریافت کنید.

vcldeveloper
چهارشنبه 17 خرداد 1391, 17:15 عصر
خب اینکه دوباره شد همون کد قبلی! شما وقتی از OmniThread.Future استفاده می کنید، نیازی نیست که خودتون Thread بسازید؛ بلکه فقط کد تابع مورد نظر رو برای Future تعریف می کنید، و مقدار Value مربوط به Future رو هر جا که لازم داشتید، میگیرید. Future خودش به طور خودکار در پشت صحنه Thread ایجاد میکنه، و تابع شما رو اجرا می کنه. هر وقت هم که خروجی تابع آماده بشه، اون رو در Value ذخیره میکنه. الان این کد جدید شما یعنی اینکه شما درباره مفهوم Future به اندازه کافی تحقیق نکردید.

Mask
پنج شنبه 18 خرداد 1391, 19:57 عصر
ممنون از راهنماییتون.
آخه مشکل من اینه که این تردی که من ساختم ، یه ترد معمولی که نیست . کلی constructor رو override کردم و پارامتر بهش دادم.
حالا چطوری با این OmniThread.Future این پارامترها و عملیات رو انجام بدم؟
اگه قرار باشه ترد TSumThread رو نسازم و به جاش OmniThread.Future مدیریت ساخت رو به عهده بگیره ، پس پارامترهای مورد نیاز ، چی میشه؟
ممنون ، از توجهتون.

vcldeveloper
جمعه 19 خرداد 1391, 20:52 عصر
پس پارامترهای مورد نیاز ، چی میشه؟


چیزی نمیشه، هر کدوم رو که تعریف کردید، در داخل همون تابع که به Future میدید قابل استفاده خواهد بود.

BORHAN TEC
شنبه 20 خرداد 1391, 02:08 صبح
در مورد آیندگان(!) که از این تاپیک استفاده خواهند کرد بر خودم لازم می دانم که بگویم یک فیلم آموزشی جالب در مورد OTL (به عبارتی Omni Threading Library) در وبسایت زیر وجود دارد که دوستان برای آشنایی بیشتر با این کتابخانه ارزشمند می توانند به آن مراجعه کنند:
www.vdug.org (http://www.vdug.org)

Mask
یک شنبه 28 خرداد 1391, 19:58 عصر
با توضیحات شما کد رو تغییر دادم :

unit Unit1;

interface

uses
Windows, Classes, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
IdRawBase,IdRawClient, IdIcmpClient, Messages, SysUtils, Variants, Graphics,
Controls, Forms, Dialogs, StdCtrls, ExtCtrls,OtlParallel;

type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
Seq: Integer=0;

implementation

{$R *.dfm}

function PS_System(HostP: string; PortP: Integer): Boolean;
var
Future : IOmniFuture<Boolean>;
begin
Future := TOmniFuture<Boolean>.Create(
function : Boolean
var
FIcmpClient: TIdIcmpClient;
FIdTCPClient: TIdTCPClient;
FActiveServer: Boolean;
HostT: string;
PortT: Integer;
SeqT: Integer;
begin
FIcmpClient := nil;
FIdTCPClient := nil;
Inc(Seq);
HostT := HostP;
PortT := PortP;
SeqT := Seq;

FIcmpClient := TIdIcmpClient.Create;
try
with FIcmpClient do
begin
ReceiveTimeout := 5000;
PacketSize := 28;
Host := HostT;
try
Ping(HostT, SeqT);
FActiveServer := (ReplyStatus.BytesReceived > 0);
except
FActiveServer := False;
end;
end;

if not FActiveServer then
Result:=false;

if FActiveServer then
begin
FIdTCPClient := TIdTCPClient.Create;
with FIdTCPClient do
begin
Host := HostT;
Port := PortT;
UseNagle := False;
try
Connect;
IOHandler.WriteLn('Hello');
Result:=True;
except
Result:=False;
end;
end;
end;

finally
if Assigned(FIcmpClient) then
FIcmpClient.Free;
if Assigned(FIdTCPClient) then
begin
if FIdTCPClient.Connected then
FIdTCPClient.Disconnect;
FIdTCPClient.Free;
end;
end;
end);
Result := Future.Value;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Memo1.Clear;
if PS_System(Edit1.Text,4321)then
Memo1.Lines.Add(Edit1.Text+' yes')
else
Memo1.Lines.Add(Edit1.Text+' no');
if PS_System(Edit2.Text,4321)then
Memo1.Lines.Add(Edit2.Text+' yes')
else
Memo1.Lines.Add(Edit2.Text+' no');
if PS_System(Edit3.Text,4321)then
Memo1.Lines.Add(Edit3.Text+' yes')
else
Memo1.Lines.Add(Edit3.Text+' no');
if PS_System(Edit4.Text,4321)then
Memo1.Lines.Add(Edit4.Text+' yes')
else
Memo1.Lines.Add(Edit4.Text+' no');
if PS_System(Edit5.Text,4321)then
Memo1.Lines.Add(Edit5.Text+' yes')
else
Memo1.Lines.Add(Edit5.Text+' no');
end;

end.
اینبار برنامه خروجی درست رو نشون میده. اما برنامه انگار تک ترد شد. چون تا مادامی که عملیات تموم نشده. برنامه قفل میمونه و دونه دونه به درخواستها رسیدگی میشه.
اگه ممکنه بیشتر توضیح بدید.
ممنون.

Mask
چهارشنبه 07 تیر 1391, 13:47 عصر
سلام.
من کد بالا رو نوشتم. فکر میکردم ، دلیل اینکه برنامم قفل میشه و دونه دونه به ترد ها رسیدگی میشه ، عیب از کد نویسی منه.
اما کد شما رو هم که یه sleep توش انداختم ، همون نتیجه رو داد.
مگه قرار نبود هر تردی برای خودش باشه و برنامه رو قفل نکنه ؟، پس چرا نتیجه اون نشد؟

uses
OtlParallel;

function IsSumDone(const Num1T, Num2T: string): Boolean;
var
Future : IOmniFuture<Boolean>;
begin
Future := TOmniFuture<Boolean>.Create(function: Boolean
var
V1, V2 : Integer;
begin
sleep(2000);
Result := TryStrToInt(Num1T, V1) and TryStrToInt(Num2T, V2);
end);
Result := Future.Value;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
if IsSumDone('a','2') then
Memo1.Lines.Add('yes')
else
Memo1.Lines.Add('no');
if IsSumDone('1','2') then
Memo1.Lines.Add('yes')
else
Memo1.Lines.Add('no');
if IsSumDone('a','d') then
Memo1.Lines.Add('yes')
else
Memo1.Lines.Add('no');
if IsSumDone('6','2') then
Memo1.Lines.Add('yes')
else
Memo1.Lines.Add('no');
end;

vcldeveloper
چهارشنبه 07 تیر 1391, 14:04 عصر
مگه قرار نبود هر تردی برای خودش باشه و برنامه رو قفل نکنه ؟
برادر من، مشکل از درک نکردن مفهوم Future هست؛ من توی همین تاپیک چند بار به این مشکل اشاره کردم، ولی شما توجه نکردید.

Future از لحظه تعریفش یک Thread جدید درست میکنه، و اجرای اون شروع میشه؛ اما اگر شما سعی کنید به خصوصیت Value اون دسترسی پیدا کنید، دو حالت پیش میاد:
1- کار Future تموم شده، و جواب شما آماده هست؛ خب شما جواب رو از Value می گیرید.
2- کار Future تموم نشده، پس جواب آماده نیست. در اون حالت Thread شما تا لحظه اتمام کار Future باید منتظر بمونه.

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

Mask
چهارشنبه 07 تیر 1391, 14:10 عصر
برادر من، مشکل از درک نکردن مفهوم Future هست؛ من توی همین تاپیک چند بار به این مشکل اشاره کردم، ولی شما توجه نکردید.

Future از لحظه تعریفش یک Thread جدید درست میکنه، و اجرای اون شروع میشه؛ اما اگر شما سعی کنید به خصوصیت Value اون دسترسی پیدا کنید، دو حالت پیش میاد:
1- کار Future تموم شده، و جواب شما آماده هست؛ خب شما جواب رو از Value می گیرید.
2- کار Future تموم نشده، پس جواب آماده نیست. در اون حالت Thread شما تا لحظه اتمام کار Future باید منتظر بمونه.

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

ممنون از استاد خودم.
پس راه حل چیه که این مشکل مرتفع شود؟
اگه تو مثال خودتون این تغییر رو انجام بدید ، ممنون میشم.