PDA

View Full Version : سوال: نحوه عملکرد Thread



ashoori
چهارشنبه 29 دی 1389, 20:08 عصر
باسلام
سوال : آیا پس از اینکه تابع execute یک thread اجرا شد ، آن thread ترمینیت می شود!؟
پاسخ احتمالا قریب به یقین مثبت است! و حتما resume هم نمی توان کرد...
حال بفرمایید بغیر از create کردن دوباره thread راهی وجود ندارد تا آن thread را دوباره تکرار کرد!؟
و اینکه چرا پس از destroy شدن thread همچنان مقدار thread.threadid برابر nil نمی باشد!؟
پیشتر از راهنماییتان سپاسگزارم.

Felony
چهارشنبه 29 دی 1389, 22:28 عصر
آیا پس از اینکه تابع execute یک thread اجرا شد ، آن thread ترمینیت می شود!؟
اگر متد FreeOnTerminate رو True کرده باشید بعد از اجرای کدهای نوشته شده و توابع صدا زده شده در متد Execute و پایان کار آنها Thread به صورت خودکار آزاد میشه ، در غیر این صورت خیر .

با این توضیح به جواب باقی سوالاتتون هم باید رسیده باشید .

vcldeveloper
چهارشنبه 29 دی 1389, 23:39 عصر
آیا پس از اینکه تابع execute یک thread اجرا شد ، آن thread ترمینیت می شود!؟
بله. البته Terminate شدن به معنی آزاد شدن شی TThread ساخته شده نیست، مگه اینکه FreeOnTerminate هم مقدارش True باشه.


و حتما resume هم نمی توان کرد...
خیر.


حال بفرمایید بغیر از create کردن دوباره thread راهی وجود ندارد تا آن thread را دوباره تکرار کرد!؟
اگر کار شما باید مرتبا تکرار بشه، در داخل متد Execute اون Thread یک حلقه بزارید که مرتبا کار شما را اجرا کنه. برای کنترل اجرای یک Thread، و اینکه اون حلقه مربوطه به واسطه چه عواملی متوقف بشه، یا چه عواملی باعث انتظار Thread بشه، می تونید از Synchronization Object های مختلف استفاده کنید. ساده ترینش برای کنترل اجرای یک Thread، استفاده از Event هست. البته منظور Event به عنوان Synchronization Object هست، نه اون Event موجود در کنترل های مختلف مثل OnClick و غیره. تعدادی از این Synchronization Object ها رو می تونید در یونیت SyncObjs پیدا کنید.

ashoori
چهارشنبه 29 دی 1389, 23:44 عصر
علی جان فرمایش شما متین ولی بنده FreeOnTerminate رو هم true کردم ولی همچنان مقدار threadid همچنان همان مقدار قبل از destroy شدنه!؟
این یعنی چی ، یعنی این که هنوز متغییر ما free یا بعبارتی nil نشده! چرا!؟

vcldeveloper
پنج شنبه 30 دی 1389, 00:22 صبح
ولی بنده FreeOnTerminate رو هم true کردم ولی همچنان مقدار threadid همچنان همان مقدار قبل از destroy شدنه!؟
مقدار ThreadID بعد از Destroy شدن در دسترس نیست، چون ThreadID یک فیلد Instance از کلاس TThread هست. اگر اون شی آزاد بشه، به فیلدش دسترسی ندارید. مقدار ThreadID بعد از Terminate شدن، و قبل از Destroy شدن، همان مقدار اولیه هست، چون مقدار ThreadID یک بار در زمان Create شدن تعیین میشه، و تا زمان Destroy شدن تغییر نمیکنه.
شما چه نیازی به ThreadID یک Thread ایی که Terminate شده، دارید؟

ashoori
پنج شنبه 30 دی 1389, 00:32 صبح
فقط برای اطمینان از اینکه thread آزاد شده یا نه!؟
قاعدتا وقتی شی ئی آزاد میشه نباید دسترسی به متغییرهای اون داشت و هم اینکه مقداری هم برایش متصور نباید شد!
لکن وقتی می بینم بعد از destroy شدن thread همچنان به متغییر و مقدار آن دسترسی وجود داره ، بنابراین حتما جایی قابل استفاده خواهد بود!!!
این ابهام رو چجور باید جواب بدم!؟

vcldeveloper
پنج شنبه 30 دی 1389, 03:03 صبح
فقط برای اطمینان از اینکه thread آزاد شده یا نه!؟
قاعدتا وقتی شی ئی آزاد میشه نباید دسترسی به متغییرهای اون داشت و هم اینکه مقداری هم برایش متصور نباید شد!
وقتی شی ایی آزاد میشه، محتوای حافظه ایی که اشغال کرده، بلافاصله از بین نمیره، از طرف دیگه، مقدار اشاره گرش هم به طور خودکار nil نمیشه. در بهترین حالت، وقتی شما از طریق همون اشاره گر سعی می کنید به اون شی دسترسی پیدا کنید، یک Access Violation می گیرید.

در ضمن، شما کی وضعیت Thread رو چک می کنید؟ ممکنه در زمانی که شما دارید وضعیت Thread رو چک می کنید، Thread هنوز در حال اجرا باشه. به طور کلی، وقتی FreeOnTerminate را True می کنید، یعنی دیگه نمیخواید وضعیت Thread را چک کنید، وقتی کارش تمام شد، میخواید بیاندازیدش دور. اگر میخواید بعد از اتمام کارش، خودتان وضعیتش را چک کنید، اون خصوصیت را غیر فعال کنید، و خودتان شی مربوطه را در کدتان Free کنید.

ashoori
پنج شنبه 30 دی 1389, 03:52 صبح
یه سوال:
فرض کنیم برنامه ما قصد دارد کنترل کند وقتی دسترسی به اینترنت وجود دارد اطلاعاتی را رد و بدل کند کدام سناریو بهتر است:
1- در thread اصلی برنامه تایمر قرار دهیم و تایمر هر باره thread را بسازد و خود thread کنترل ها و تبادل را انجام دهد.
2- thread یک بار ساخته شود و در execute در حلقه ای اتصال را کنترل کرده و در صورت انجام تبادل اطلاعات یا بسته شدن برنامه اصلی به کار خود ادامه دهد.
و یا سناریوی (های) دیگر!؟
باتشکر

vcldeveloper
پنج شنبه 30 دی 1389, 13:50 عصر
حالت دوم، چون ایجاد Thread یک کار با سربار بالا محسوب میشه، و نباید برای همچین کارهایی مرتبا Thread ایجاد کنید و آزاد کنید.


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

ashoori
پنج شنبه 30 دی 1389, 14:43 عصر
باتشکر
یقینا ما thread را هنگام closequery یا destroy شدن فرم اصلی terminate یا free خواهیم کرد.
منظور من این بود که thread چندبار در بازه زمانی مثلا هر یک دقیقه (ontimer) ساخته بشه تا زمانی که پس از اتصال به اینترنت تبادل اطلاعات کنه بهتره .... یا اینکه یک بار ساخته بشه و تا پایان تبادل اطلاعات در حلقه ای در execute کار پیگیری شود.
در حقیقت آیا ساختن هرباره thread منابع سیستم رو خیلی درگیر نمیکنه!؟
و ضمنا این که غیر از تایمر و حلقه که کنترل کنند اتصال به اینترنت را ، راهی وجود داره که وقتی در حین کار برنامه ، اتصال به اینترنت برقرار میشه ، به برنامه پیغامی ارسال بشه و اونوقت thread تبادل اطلاعات ایجاد و شروع به کار کنه!؟
بازهم متشکرم (در ضمن علی آقا این باقی میمانه رو خوب اومدی:لبخندساده:)

vcldeveloper
پنج شنبه 30 دی 1389, 18:53 عصر
منظور من این بود که thread چندبار در بازه زمانی مثلا هر یک دقیقه (ontimer) ساخته بشه تا زمانی که پس از اتصال به اینترنت تبادل اطلاعات کنه بهتره .... یا اینکه یک بار ساخته بشه و تا پایان تبادل اطلاعات در حلقه ای در execute کار پیگیری شود.
همون یک بار که ایجادش کنید، بهتر هست.


و ضمنا این که غیر از تایمر و حلقه که کنترل کنند اتصال به اینترنت را ، راهی وجود داره که وقتی در حین کار برنامه ، اتصال به اینترنت برقرار میشه ، به برنامه پیغامی ارسال بشه و اونوقت thread تبادل اطلاعات ایجاد و شروع به کار کنه!؟
بله، باید قبل از ایجاد Thread، مقدار Event ایی که در اون Thread ازش به عنوان Synchronization Object استفاده می کنید را Non-Signal کنید. و وضعیت سیگنال اون Event را در داخل متد Execute اون Thread چک کنید. این کار باعث میشه که Thread تا زمان Signaled شدن اون Event به خواب بره. هر زمان که چیزی برای ارسال توسط Thread آماده کردید، اون Event را در Thread اصلی Signaled می کنید. این کار باعث میشه که اون Thread از خواب خارج بشه، و پردازشش را انجام بده. باید دوباره Event را Non-Signaled کنید، تا بعد از اتمام ارسال داده ها، وقتی اجرای Thread به ابتدای حلقه میرسه، مجددا Thread به خواب فرو بره.

ashoori
جمعه 01 بهمن 1389, 17:24 عصر
باسلام و تشکر از توضیحات جالبتون.
با عرض معذرت! خواهشمند است در صورت امکان، با نمونه مثال بیشتر فهم مطلب بفرمایید.

vcldeveloper
جمعه 01 بهمن 1389, 19:06 عصر
در صورت امکان، با نمونه مثال بیشتر فهم مطلب بفرمایید.

این کد یک Thread هست که از یک Event برای کنترل اجرای خودش استفاده میکنه:



unit Unit5;

interface

uses
Classes {$IFDEF MSWINDOWS} , Windows {$ENDIF};

type
TTestThread = class(TThread)
private
FEvent : THandle;
protected
procedure DoTheJob;
procedure Execute; override;
public
constructor Create(Suspended: Boolean);
destructor Destroy; override;
procedure NotifyThread;
procedure StopThread;
end;

implementation

{ TTestThread }

constructor TTestThread.Create(Suspended: Boolean);
begin
inherited;
FEvent := CreateEvent(nil,True,False,'');
end;

destructor TTestThread.Destroy;
begin
CloseHandle(FEvent);
inherited;
end;

procedure TTestThread.StopThread;
begin
Terminate;
SetEvent(FEvent);
WaitFor;
end;

procedure TTestThread.DoTheJob;
begin
if not Terminated then
OutputDebugString('This is a test');
end;

procedure TTestThread.Execute;
begin
NameThreadForDebugging('TestThread');

while not Terminated do
begin
if WaitForSingleObject(FEvent, INFINITE) = WAIT_OBJECT_0 then
DoTheJob;
end;
end;

procedure TTestThread.NotifyThread;
begin
PulseEvent(FEvent);
end;

end.


این Thread کار خاصی انجام نمیده، فقط منتظر میمونه که Event مربوطه Signaled بشه. اگر این اتفاق بیافته، متد DoTheJob اجرا میشه. این متد هم کار خاصی انجام نمیده، فقط یک متن ساده را در پنجره Event Log دیباگر دلفی نمایش میده. برای اینکه Event مربوطه Signaled بشه، Thread اصلی باید متد NotifyThread را فراخوانی کنه. متد NotifyThread، تابع PulseEvent را اجرا میکنه. این تابع یک لحظه Event مورد نظر را Signaled میکنه، تا Thread ما از انتظار خارج بشه. بلافاصله که Thread از انتظار خارج بشه، PulseEvent وضعیت Event را به Non-sginaled تغییر میده. این کار باعث میشه که بعد از اتمام اجرای DoTheJob، و ادامه حلقه، Thread ما مجددا متوقف بشه؛ یعنی با هر بار اجرای NotifyThread، فقط یک بار DoTheJob اجرا میشه. در سایر مواقع، Thread ما بیکاره و منتظر نشسته.

وقتی کارمان با این Thread تمام میشه، باید Thread را Terminate کنیم، اما از آنجایی که Thread هنوز منتظر Event ما هست، و در خواب به سر میبره، متوجه تغییر وضعیت Terminated نمیشه. باید Thread را از حالت خواب خارج کنیم، تا متوجه بشه. برای همین یک متد دیگه داریم با نام StopThread. این متد ابتدا مقدار Terminated را True میکنه، سپس Event ما را Signaled میکنه، تا Thread از خواب بیدار بشه. با بیدار شدن Thread، متوجه میشه که Terminated شده، پس از حلقه خارج میشه، و به اجرای خودش خاتمه میده. وقتی اجرای Thread خاتمه پیدا کرد، میشه اون رو Free کرد.

این میشه کاری که شما می خواستید. یک روش بهتر استفاده از مدل Producer - Consumer هست. در اون حالت، شما یک صف داده دارید، که یک Thread در اون داده می نویسه، و Thread دیگه هر زمان که داده ایی وارد صف بشه، از خواب خارج میشه، و اون داده را پردازش میکنه. هر زمان که صف خالی بشه، اون Thread مجددا به خواب میره. پیاده سازی Producer - Consumer از کد فعلی پیچیده تر هست. من حوصله نداشتم اون رو مثال بزنم. می تونید درباره اش در اینترنت مطلب بخونید. مطالب زیادی درباره اش پیدا می کنید.

ashoori
جمعه 01 بهمن 1389, 22:14 عصر
اجرکم علی الله...




while not Terminated do
begin
if WaitForSingleObject(FEvent, INFINITE) = WAIT_OBJECT_0 then
DoTheJob;
end;

با این تفاسیر بفرمایید، حلقه مذکور چه وضعیتی رو خواهد داشت!؟ آیا این حلقه تا مادامی که thread ترمینت نشده ادامه داره و هر دفعه شرط رو چک میکنه یا اینکه حلقه پس از رسیدن به شرط متوقف میشود تا Waits until the specified object is in the signaled state or the time-out interval elapses.

ashoori
جمعه 01 بهمن 1389, 23:48 عصر
اون Event را در Thread اصلی Signaled می کنید. این کار باعث میشه که اون Thread از خواب خارج بشه، و پردازشش را انجام بده. باید دوباره Event را Non-Signaled کنید، تا بعد از اتمام ارسال داده ها، وقتی اجرای Thread به ابتدای حلقه میرسه، مجددا Thread به خواب فرو بره.
طبق فرمایش شما ، دستور PulseEvent(FEvent); باعث Signaled و Non-Signaled شدن event میشه!؟
چون در توضیح PulseEvent اومده Sets the specified event object to the signaled state and then resets it to the nonsignaled state after releasing the appropriate number of waiting threads.

ashoori
شنبه 02 بهمن 1389, 00:09 صبح
حال اگر مقدار dwMilliseconds را در WaitForSingleObject مقدار دهی عددی کنیم، thread تا همان مقدار منتظر می ماند و سپس بکارش ادامه می دهد یا اینکه signaled بشه! بنابراین اگه بوسیله pulseEvent ، سینگنالد نشده باشه دوباره به خواب نمیره! در این حالت چه راهی را پیشنهاد می فرمایید تا پس از اجرای کد دوباره به همان میزان میلی ثانیه مورد نظر منتظر بمونه!!!؟
The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled or the interval elapses.

vcldeveloper
شنبه 02 بهمن 1389, 02:16 صبح
با این تفاسیر بفرمایید، حلقه مذکور چه وضعیتی رو خواهد داشت!؟ آیا این حلقه تا مادامی که thread ترمینت نشده ادامه داره و هر دفعه شرط رو چک میکنه یا اینکه حلقه پس از رسیدن به شرط متوقف میشود تا
Waits until the specified object is in the signaled state or the time-out interval elapses.

بعد از رسیدن به شرط تا زمان Signaled شدن Event، به خواب میره، و چیزی را اجرا نمیکنه.


حال اگر مقدار dwMilliseconds را در WaitForSingleObject مقدار دهی عددی کنیم، thread تا همان مقدار منتظر می ماند و سپس بکارش ادامه می دهد یا اینکه signaled بشه! بنابراین اگه بوسیله pulseEvent ، سینگنالد نشده باشه دوباره به خواب نمیره! در این حالت چه راهی را پیشنهاد می فرمایید تا پس از اجرای کد دوباره به همان میزان میلی ثانیه مورد نظر منتظر بمونه!!!؟
اگر به dwMilliseconds مقداری غیر از INFINITE بدید، اگر بعد از طی شدن زمان مشخص شده، Event مورد نظر Signaled نشه، خروجی WaitForSingleObject میشه WAIT_TIMEOUT، و شرط مربوطه برقرار نمیشه، پس بدون اجرا شدن DoTheJob، یک بار حلقه گردش میکنه. این کار ارزشی نداره، فقط باعث افزایش بار CPU میشه.

mohsen24000
شنبه 02 بهمن 1389, 12:10 عصر
سوال: وقتی پس از thread.create ما اون رو resume کنیم ، بفرمایید thread در کدام حالات signaled یا non-signaled از حالت suspend خارج میشه؟
بعبارتی به محض اینکه resume بشه شرط اجرای تابع dothejob محقق میشه (WAIT_OBJECT_0)!؟
باتشکر از راهنمایی که میفرمایید
.................................................. ............
هرچند بنده با تستی که انجام دادم بعد از resume شدن thread تا مادامی که notify نشده ، signaled نمیشه!

vcldeveloper
شنبه 02 بهمن 1389, 18:01 عصر
سوال: وقتی پس از thread.create ما اون رو resume کنیم ، بفرمایید thread در کدام حالات signaled یا non-signaled از حالت suspend خارج میشه؟
Thread خودش این حالت ها را نداره، بلکه این حالت ها مربوط به اون Synchronization Object مورد استفاده شما ست، که در اینجا ما از Event استفاده کردیم.

Suspend کردن Thread با استفاده از تابع SuspendThread، یا متد TThread.Suspend باعث توقف اجرای Thread میشه. البته Suspend و Resume جزو Synchronization Objects نیستند، و نباید از اون ها برای کنترل اجرای Thread استفاده کرد. این توابع بیشتر به درد کار دیباگرها میخورند.