PDA

View Full Version : سوال: استفاده از thread برای دریافت اطلاعات با idhttp



iamehsan56
سه شنبه 08 بهمن 1392, 18:57 عصر
با سلام
من حدود 100 تا ip دارم که با idhttp باید بهشون وصل بشم و دایما از همشون data دریافت کنم
به این ترتیب باید از thread استفاده کنم
من با thread ها قبلا کار کردم
اما نمی دونم برای این مورد چطور استفاده اش کنم
به نظر خودم اینطور رسید که در ابتدا به ازای هر ip یه thread در runtime ایجاد کنم و thread ,وقتی execute بشه شروع می کنه به گرفتن اطلاعات از اون ip . هر thread رو هم با یه timer با یه فاصله زمانی صدا می زنم .
مشکلی که ایجاد می شه اینه که تو کلاس thread یه تابع execute دارم که قراره به همه ip ها وصل بشه .چطوری باید ip رو به اون thread پاس کنم .
لطفا کمک فرمایید

Mask
سه شنبه 08 بهمن 1392, 20:37 عصر
هر thread رو هم با یه timer با یه فاصله زمانی صدا می زنم .
نیازی به اینکار نیست. شما میتونید در یک دکمه و با یک جلقه هر تعداد ترد میخواهید بسازید و آی پی مورد نظر رو بهش اختصاص بدید.
برای اینکه بتونید هر ترد رو با آی پی اختصاصی ران کنید ، باید constractor ترد رو overide کنید. و یک پارامتر ورودی به عنوان آی پی به تردها بدی .
اینجوری هر ترد با آی پی خودش کار میکنه.

iamehsan56
شنبه 12 بهمن 1392, 14:42 عصر
ممنون از پاسختون
1.می شه یه کد نمونه برای override کردن بذارید
2. به هر صورت برای این که خوندن از ip دایم صورت بگیره احتیاج به یه تایمر برای هر thread هست تا ترد execute بشه ؟ این طور نیست ؟ در غیر این صورت با اجرای یه ترد اینکار یه بار صورت می گیره و تموم می شه


در صمن طبق گفته شما من این کارو کردم




unit Unit2;

interface

uses
Classes;

type
my = class(TThread)
public
tip:string;
constructor Create(Suspend:Boolean;ip:string);
destructor Destroy;

private
{ Private declarations }
protected
procedure Execute; override;
end;

implementation
uses unit1;


procedure my.Execute;
begin
{ Place thread code here }
form1.Memo1.Lines.Add(tip);
end;
constructor my.Create(Suspend:Boolean;ip:string);
begin
tip:=ip;
inherited Create(false);
///
end;
end.


destructor my.Destroy;
begin

inherited;
end;







procedure TForm1.butclick(Sender: TObject);
begin
t:=My.Create(true,'192.168.1.'+inttostr(i));
t.FreeOnTerminate := True;
t.Resume;
end;



این منظور شما بود ؟
من ip رو از طریق create پاس دادم . و ده تا thread رو run کردم
اگه این درسته حالا باید واسه هر ترد یه تایمر داشته باشم که این کارو دایم انجام بده یعنی thread دایم run بشن. درسته ؟؟

Mask
دوشنبه 14 بهمن 1392, 19:42 عصر
در مورد override درست انجام دادید. مشکلتون کجاست؟

iamehsan56
سه شنبه 15 بهمن 1392, 08:31 صبح
منظورم استفاده از timer هستش . می خوام ببینم روش درستیه . این که با تایمر دایما thread رو run کنم . یا روش بهتری هم هست ؟
در این روش باید وقتی thread رو ایجاد می کنم به ازای هر thread یه تایمر هم در runtime ایجاد کنم و Ontimer رو معادل زیر قرار بدم


t:=My.Create(false,'192.168.1.'+inttostr(i));


در انتها هم باید timer هارو free کنم
سوال اولم اینه که این روشم درسته
سوال دوم این که timer ها رو کی باید free کنم . اگه تو onclose این کارو بکنم اگه مثلا برنامه درست بسته نشه مثلا end task بشه تایمر ها از پاک نمی شن و موجب کم شدن resource های windows می شه . ایا درست فکر می کنم ؟ راهنمایی بفرمایید استاد

loo30fer
سه شنبه 15 بهمن 1392, 11:21 صبح
منظورم استفاده از timer هستش . می خوام ببینم روش درستیه . این که با تایمر دایما thread رو run کنم . یا روش بهتری هم هست ؟
بهتره داخل همون thread اقدام به Ping کردن آیپی بکنید و با کمک Sleep یک وقفه در Ping بعدی ایجاد کنید.
در ضمن در Thread نباید مستقیم با فرم ارتباط برقرار کنید.

iamehsan56
سه شنبه 15 بهمن 1392, 11:27 صبح
یعنی منظورتون اینه که یه حلقه دایم تو هر thread ایجاد کنم ؟

iamehsan56
چهارشنبه 16 بهمن 1392, 19:49 عصر
من جواب پست قبلمو نگرفتم هنوز


یعنی منظورتون اینه که یه حلقه دایم تو هر thread ایجاد کنم ؟

با این وجود همین کارو کردم. حالا از بیرون ترد اگه بخوام یکی از تردارو terminate کنم چطور اینکارو بکنم . من تردارو یکجا ایجاد کردم


for i:=1 to 10 do
begin
t:=My.Create(true,'192.168.1.'+inttostr(i));
t.FreeOnTerminate := True;
t.Resume;
end;

حالا وقتی بخوام پنجمین تردو terminate کنم چطور این کارو بکنم . از چه روشی ؟

Mask
چهارشنبه 16 بهمن 1392, 21:35 عصر
چه نیازی به ترمینیت ترد دارید؟

iamehsan56
چهارشنبه 16 بهمن 1392, 22:11 عصر
وقتی بخوام برنامه رو ببندم باید تردارو تموم کنم
جدا از این به لحاظ فنی خاتمه ترد تو این حالت میسر نیست ؟

joker
پنج شنبه 17 بهمن 1392, 10:49 صبح
میتونی در حلقه while بینهایتی که برای هر ترد نوشتی یک متغییر عمومی در نظر بگیری که هر وقت از توی فرم اصلیت مقدارشو عوض کردی حلقه بینهایت بپره بیرون abort
البته توی دلفی وقته برنامه را ببندی خودش ترتیب تمام تردها و اشیاء را میده و آزادشون میکنه نگرانی نداره ولی اگه اصرار به این کار داری روش بالا میتونه کارت را حل کنه.

iamehsan56
پنج شنبه 17 بهمن 1392, 12:12 عصر
1. اگه من وسط دریافت دیتا با idhttp باشم و برنامه همون موقع بسته بشه ، اونوقط برنامه error می ده .
پس باید تردارو terminate کنم . درست نمی گم ؟

2. من وقتی با یه حلقه چند ترد ایجاد و run می کنم ، وقتی با یه متغییر عمومی درخواست stop ترد رو می کنم فقط یکی از تردا خارج می شه .
این یعنی چه ؟

این کد من تو execute هست


while not form1.stop do
begin
if not Check_Ping (tip) then continue;
myht:=TIdHTTP.Create(nil);

try
myht.Request.Clear;
myht.Request.BasicAuthentication:= true;
myht.Request.UserName := 'admin';
myht.Request.Password := '5555';

Cache:= myht.Get('http://'+tip+'/yekta.cgi');

finally
myht.Free;
end;

CoInitialize(nil);
try

Doc := coHTMLDocument.Create as IHTMLDocument2; // create IHTMLDocument2 instance
V := VarArrayCreate([0,0], varVariant);
V[0] := Cache;
Doc.Write(PSafeArray(TVarData(v).VArray));
finally
CoUninitialize;
end;


searchrow:=form1.sitegrid.Search(tip);
if searchrow<0 then exit;

for i:=searchrow+1 to form1.sitegrid.GetNodespan(searchrow)-1+searchrow do
begin

Elem := GetElementById(Doc, 'SignWord'+inttostr(i-searchrow)) as IHTMLElement;
if Assigned(Elem) then
//form1.grid.Cells[tid,i-searchrow]:=Elem.innerHTML;
form1.sitegrid.Cells[3,i]:=Elem.innerHTML;
end;

sleep(1000);

end;

form1.memo1.Lines.Add( inttostr(form1.t.ThreadID ));



من thread id رو موقع خروج تو یه memo اضافه می کنم . اما فقط یه thread id اضافه می شه (چرا فقط یه دونه . در حالی که من 10 تا ترد رو می سازم) . علاوه بر این thread id اضافه شده متعلق به آخرین تردی هست که ساختم . چرا اینطوری می شه ؟
کمک کمک لطفا

joker
پنج شنبه 17 بهمن 1392, 15:04 عصر
فرضا میگیریم روتین تردت اینه :


procedure TxThread.Execute;
var
a:integer;
begin

While 1=1 Do
begin
if ALLThreadStop then break;
....
کدهای دریافت اطلاعات و ارسال با indy
.....
end;// wile
......
end;


""1. اگه من وسط دریافت دیتا با idhttp باشم و برنامه همون موقع بسته بشه ، اونوقط برنامه error می ده .""
متغییر ALLThreadStop را یک متغییر از نوع boolean تعریف کن و تمام. هر وقت مقدارش را not کنی ، ترد پس از انجام عملیات داخل وایل برمیگرده و چک میکنه اگه این متغییر تغییر کرده بود دیگه حلقه اجرا نمیشه و عملا ترد شما خاتمه یافته میشه. پس هم در حین عملیات چیزی استوپ نمیشه و هم به محض خاتمه پردازش ترد دیگه اجرا نمیشه

این لینکم ببین : http://www.shabgard.org/forums/showthread.php?25353

iamehsan56
پنج شنبه 17 بهمن 1392, 18:27 عصر
درسته . من ALLThreadStop را برای خاتمه استفاده می کنم اما در خروج از برنامه باید چک کنم که همه تردا خاتمه پیدا کردن . اینکارو چطور انجام بدم؟ من ارایه به تعداد تردا ایجاد کردم که هر کدوم وقتی کارشون تموم شد وضعیتشون رو تو اون می ذارن . و اینجوری هر وقت کل تردا خانمه یافت برنامه می تونه بسته شه . درسته ؟



سول بعدیم اینه که من کلی مطلب تو سایت و منابع دیگه خوندم که می گن به منابع و حافظه در فرم اصلی نباید از داخل ترد مستقیم دسترسی داشت و باید از sync استفاده کرد
بعد یه جایی خوندم که استفاده از sync خوب نیست و باید برای استفاده از منابع فرم وقتی چندین ترد می خوان دسترسی داشته باشن باید از critical section استفاده کرد .
و ارسال message به فرم اصلی اطلاعات می فرستن . من گیج شدم که کار درست چیه ؟

بعد این که من تو همین برنامه ای که دارم می نویسم هر ترد اطلاعات یه ip رو می گیره که 100 تا مثلا پارامتر دریافت می کنه . من یه آرایه ای از رکورد درست کردم .
هر ترد باید یه بخش از ارایه رو که مربوط به خودشه اپدیت کنه . نمی دونم چطوری می تونم به ارایه از هر ترد دسترسی داشت
ممنون می شم راهنمایی بفرمایید اساتید محترم

mySite:array[1..Max_site_Num] of TSite ;



type
TSite = record
SiteIP: string;

ParamValue: array[1..130] of string;


end;

iamehsan56
شنبه 19 بهمن 1392, 18:58 عصر
من تو پست زیر راجع به criticalsection خوندم
http://barnamenevis.org/showthread.php?438435-VirtualTreeView
اما یه چیزی رو متوجه نشدم . متغیز از نوع tcriticalsection رو باید کجا تعریفش کر ؟ تو فرم اصلی یا داخل ترد ؟
یعنی به شکل عمومی توفرم اصلی ، یا عمومی تو ترد
من یه جایی دیدم به شکل یه پارامتر از طریق کانستراکتور ترد پاسش دادن


t:=My.Create(true,mycritical)

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


const
WM_OutputReady = WM_USER +1;

StrListCriticalSection.Enter;
MemoStrList.Add('Some Texts');
StrListCriticalSection.Leave;
PostMessage(MainForm.Handle,WM_OutputReady,0,0);




procedure OnOutputReady(var Msg: Tmessage); message WM_OutputReady;
.
.
.
procedure TMainForm.OnOutputReady(var Msg: Tmessage);
begin
if MemoStrList.Count>0 then
begin
StrListCriticalSection.Enter;
Memo1.Lines.AddStrings(MemoStrList)
MemoStrList.Clear;
StrListCriticalSection.Leave;
end;
end;

joker
یک شنبه 20 بهمن 1392, 10:57 صبح
1. اگه من وسط دریافت دیتا با idhttp باشم و برنامه همون موقع بسته بشه ، اونوقط برنامه error می ده .
پس باید تردارو terminate کنم . درست نمی گم ؟

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

Mask
یک شنبه 20 بهمن 1392, 13:01 عصر
در تایید صحبتهای جوکر :

بیخودی نگرانشی ، دلفی موقع بسته شدن برنامه خودش زحمت همه کارها را میکشه
try
except
هم فراموش نشه فقط جهت اطمینان.
حتی اگر شما در حالت دیزاین تایم eror هم دریافت کنید ، میتونید با try except خطای ایجاد شده رو کنترل کنید.
اما اگر باز روی این خطای حین دیزاین تاکید دارید، کافیه ترد ها رو ساسپند کنید و برنامه رو بندید ، با این روش دیگه خطایی دریافت نمیکنید.
اما اگر باز بر روی بسته شدن همه تردهای تاکید دارید :
چندین راه برای اطمینان از بسته شدن تردها هست :
هر ترد دارای id منحصر به فردی هست ، میتونید عملیات خروج برنامه رو متوقف و سپس از روی لیست با دستور زیر تردها رو بسته و سپس، پس از اطمینان از بسته شدن، برنامه رو ببندید.

TerminateThread(MyThread.ThreadId);
البته استفاده از متغیر Boolean برای خاتمه While بیشتر پیشنهاد داده میشه.
لینکهای زیر در جواب سوال شما کار سازه :
http://stackoverflow.com/questions/1203185/delphi-terminate-all-the-threads-tthread-on-closing-application
http://stackoverflow.com/questions/10401865/how-to-terminate-anonymous-threads-in-delphi-on-application-close

iamehsan56
یک شنبه 20 بهمن 1392, 14:31 عصر
ممنون از پاسخت مهربانانه استادان عزیز
من تردمو تو کد زیر گذاشتم
این برنامه حدود چند دقیقه درست کار می کنه . اما بعد از اون دیگه کار نمی کنه . یعنی دیگه عمل ping رو هم انجام نمی ده . نمی دونم تو کجای ترد گیر می افته . در این وضعیت وقتی همون پارامتر stop رو واسه تردا می فرستم تا تردارو خاتمه بدم ، از ترد بیرون نمی آد .( چون موقع بیرون اومدن هر کدوم از تردا وضعیت خودشونو تغییر می دن از طریق این کد


StopThread[tid]:=true;

نمی دونم کد من چه مشکلی داره . آیا ping من مشکلی داره . یا try except های من مشکلدارن ؟
لطفا راهنمایی فرمایید دوستا ن عزیز ؟
در رابطه با استفاده از criticalsection خواهشا یه توضیحی بدین که باید چظور و کجا تعریف بشه


procedure my.Execute;
var
myht:TIdHTTP;

V: OleVariant;
Doc: IHTMLDocument2;
Cache:string;
elem:IHTMLElement;
i,searchrow:integer;
label br;

begin



while true do
begin
sleep(1000);
if form1.stop then
begin
form1.StopThread[tid]:=true;

break;
end;





if not Check_Ping (tip) then continue;



myht:=TIdHTTP.Create(nil);

try
myht.Request.Clear;
myht.Request.BasicAuthentication:= true;
myht.Request.UserName := 'admin';
myht.Request.Password := tpass;
myht.port:=tport;

myht.readtimeout:=2000;


Cache:= myht.Get('http://'+tip+'/yekta.cgi');


finally

myht.Free;
end;



CoInitialize(nil);
try

Doc := coHTMLDocument.Create as IHTMLDocument2; // create IHTMLDocument2 instance
V := VarArrayCreate([0,0], varVariant);
V[0] := Cache;
Doc.Write(PSafeArray(TVarData(v).VArray));
finally
CoUninitialize;
end;


searchrow:=form1.sitegrid.Search(tip);
if searchrow<0 then exit;

for i:=searchrow+1 to form1.sitegrid.GetNodespan(searchrow)-1+searchrow do
begin

Elem := GetElementById(Doc, 'SignWord'+inttostr(i-searchrow)) as IHTMLElement;
if Assigned(Elem) then

form1.sitegrid.Cells[3,i]:=Elem.innerHTML;
end;



end;








end;

constructor my.Create(Suspend:Boolean;ip:string;id:integer;pas s:string;port:integer{; ALock: TCriticalSection});
begin
tip:=ip;
tport:=port;
tpass:=pass;
tid:=id;
// tlock:=ALock;

inherited Create(false);
///
end;
destructor my.Destroy;
begin

inherited;
end;


function my.GetElementById(const Doc: IDispatch; const Id: string): IDispatch;
var
Document: IHTMLDocument2; // IHTMLDocument2 interface of Doc
Body: IHTMLElement2; // document body element
Tags: IHTMLElementCollection; // all tags in document body
Tag: IHTMLElement; // a tag in document body
I: Integer; // loops thru tags in document body
begin
Result := nil;
// Check for valid document: require IHTMLDocument2 interface to it
if not Supports(Doc, IHTMLDocument2, Document) then
raise Exception.Create('Invalid HTML document');
// Check for valid body element: require IHTMLElement2 interface to it
if not Supports(Document.body, IHTMLElement2, Body) then
raise Exception.Create('Can''t find <body> element');
// Get all tags in body element ('*' => any tag name)
Tags := Body.getElementsByTagName('*');
// Scan through all tags in body
for I := 0 to Pred(Tags.length) do
begin
// Get reference to a tag
Tag := Tags.item(I, EmptyParam) as IHTMLElement;
// Check tag's id and return it if id matches
if AnsiSameText(Tag.id, Id) then
begin
Result := Tag;
Break;
end;
end;
end;


function my.Check_Ping(ip: string): Boolean;
var
MyIdIcmpClient : TIdIcmpClient;
begin
Result := True;
Application.ProcessMessages;

MyIdIcmpClient := TIdIcmpClient.Create(nil);

try

MyIdIcmpClient.ReceiveTimeout := 1000;
MyIdIcmpClient.Host := ip;
MyIdIcmpClient.TTL := 24;
MyIdIcmpClient.Protocol := 1;


try
MyIdIcmpClient.Ping(ip,tid);;
if MyIdIcmpClient.ReplyStatus.BytesReceived=0 then Result := False;

except
Result := False;

//Exit;
end;



finally
MyIdIcmpClient.Free;

end;

end;

iamehsan56
سه شنبه 22 بهمن 1392, 12:58 عصر
مشکل حل شد . من synchronize رو برای دسترسی به فرم اضافه کردم و از طرفی من تو حلقه هر بار tidhhtp , ticmp رو می ساختم و ازاد می کردم . ساخت و ازاد کردن رو از حلقه خارج کردم . مشکل حل شد .

ممنون از راهنمایی ها .
من دارم تو 100 تا ترد از صد تا ip می خونم که این کارد داره دایم انجام می شه . می خوام ببینم راه بهینه تری از اینکاری که من دارم می کنم هم هست ؟
اون صد تا ip فقط می تونن صفحه وب بدن . به همین خاطر من دارم دایم اونارو می خونم . کارش با دو سه تا ip تا اینجا خوبه ومشکلی نداشته . اما نمیدونم وقتی از 100 تا ترد از 100 تا ip بخونم چه مشکلی ممکنه ایجاد بشه .
مکنه راهنمایی بفرمایید . ایا ایده بهتری می شه به کار بست . می خوام در انتها به مشکل بنخورم
باز هم از همه راهنمایی ها ممنون

joker
سه شنبه 22 بهمن 1392, 13:19 عصر
مشکلی نداری ، نهایت محدودیت وقتی هست که به واسطه محدودیت سیستم عامل روی حداکثر تعداد ترد پیدا میکنی ( با فرض cpu -ram و پهنای باند مکفی داشتن)
ویندوز xp تا 2هزار تا ترد ( نرمال) و حداکثر 30 هزار ترد ، با دقت api ها را استفاده کردن
روی ویندوزهای 64 بیتی هم تا 60 هزارتا حدودا ترد همزمان میتونی داشته باشی

پیوست : اعداد فوق تلورانس دارند ، حدودا نوشتم.

Mask
سه شنبه 22 بهمن 1392, 13:52 عصر
در مورد کریتیکال سکشن پست جدا بزنید.