PDA

View Full Version : Threads, Events & Mutexes در سی شارپ ، قسمت پنجم



Vahid_Nasiri
پنج شنبه 27 آذر 1382, 21:35 عصر
مثال بیست و سوم :


using System.Threading;

public class yyy
{
public void abc()
{
Monitor.Enter(this);
for ( int i = 1; i<=5;i++)
{
System.Console.Write(i + " " + Thread.CurrentThread.GetHashCode() + " ");
Thread.Sleep(1000);
}
Monitor.Exit(this);
}
}

public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
Thread t1 = new Thread(new ThreadStart(a.abc));
t.Start();
t1.Start();
}
}

Output
1 2 2 2 3 2 4 2 5 2 1 3 2 3 3 3 4 3 5 3

با کمک مثال فوق می توان مفهوم عمر و طول عمر یک ترد را به خوبی توضیح داد.
تصور کنید به صورت همزمان 5 پرینتر دستور چاپ را به یک پرینتر موجود در شبکه فرستاده اند. در طول Time-slice های متوالی پرینتر چند صفحه از برنامه ی 1 و چند صفحه از برنامه ی 2 را پرینت می گیرد و الی آخر. کار پرینتر به درستی پایان پذیرفته است اما خروجی حاصل کاملا درهم و برهم است.
بنابراین در حالت ایده آل هر برنامه باید از منبع به اشتراک گذاشته شده تنها در یک زمان تا هنگامیکه کارش به پایان برسد ، استفاده نماید. برای این منظور متد چاپ پرینتر شبکه باید بگونه ای نوشته شود که تا زمانیکه یک برنامه اجرای آنرا به پایان نرسانده است ، سایر برنامه ها اجازه ی اجرای آنرا نداشته باشند و تا پایان اجرای تابع صبر نمایند.
در کد فوق فرض کنید که تابع abc حاوی کدی است که در خواستی را به پرینتر برای چاپ می فرستد. تردt در آغاز شروع به اجرای آن می نماید. تردt1 باید صبر کند تا تردt اجرای برنامه را تمام کند ( حتی اگر طول اجرای این عملیات از یک time-slice کامل بیشتر باشد (این مورد توسط پارامتر ورودی تابعSleep قابل تنظیم است) ).
این ایده توسط تابع استاتیک Enter موجود در کلاس Monitor میسر شده است.
پارامتر ورودی تابع Enter شیءایی است اشاره کننده به کلاسی که تابع مورد نظر در آن واقع شده است . در این حالت هر ترد باید صبر کند تا بتواندEnter نماید. سدها تنها در صورتی کنار می روند که تابع Exit کلاسMonitor فراخوانی شده باشد. Enter محافظی است تصمیم گیرنده در مورد تردی که مجاز به اجرای کد است. در این حالت هیچ دو تردی نمی توانند باهم یک تابع را اجرا نمایند. راه شروع شده از Enter به Exit کاملا یک طرفه است و در ضمن تنها ماشینی می تواند وارد این مسیر شود که ماشین دیگر آنرا تمام و ترک کرده باشد. با توجه به این موضوع ، نامهای Enter و Exit بسیار هوشمندانه انتخاب شده اند.



مثال بیست و چهارم :


using System;

using System.Threading;
public class ggg
{

};

public class yyy
{
public ggg g1;
public void abc( )
{
Monitor.Enter(g1);
for ( int i = 1; i <= 3; i++)
{
Console.Write(" Hell " + i);
Monitor.Wait(g1);
Console.Write(" Waitabc " + i);
Monitor.Pulse(g1);
}
Monitor.Exit(g1);
}
};

public class xxx
{
public ggg g1;
public void pqr( )
{
Monitor.Enter(g1);
for ( int i = 1; i <= 3; i++)
{
Console.WriteLine(" Everyone " + i);
Monitor.Pulse(g1);
Monitor.Wait(g1);
Console.Write(" Waitpqr " + i);
}
Monitor.Exit(g1);
}
};

public class zzz

{
public static void Main(String[] args)
{
ggg g = new ggg( );
yyy a = new yyy( );
a.g1 = g;
Thread t = new Thread(new ThreadStart(a.abc));
t.Start( );
xxx b = new xxx();
b.g1 = g;
Thread t1 = new Thread(new ThreadStart(b.pqr));
t1.Start( );
}
};


Output
Hell 1 Everyone 1
Waitabc 1 Hell 2 Waitpqr 1 Everyone 2
Waitabc 2 Hell 3 Waitpqr 2 Everyone 3
cs Waitabc 3 Waitpqr 3

در تابع Main ، شیءg ایجاد شده است که وهله ای از کلاس بدون کد ggg می باشد. سپس دو شیء xxx و yyy ایجاد شده است و بعنوان delegate به تابع فرستاده می شوند. بنابراین تردها می توانند توابع abc و pqr را اجرا نمایند. سپس ما شیء g1 را با وهله ای از کلاس ggg مقدار دهی می کنیم. g1 های موجود در xxx و yyy نمایانگر g مشابه ایجاد شده در تابع Main هستند.
بعد از مقدار دهی این اعضاء ، دو ترد فعال سازی شده اند. همانند قبل فرض کنیم که تابع abc در ابتدا فراخوانی می شود.
تابع Monitor.Enter کار همزمانی یک شیء بین تردهای مختلف را بعهده دارد و این شیء بعنوان آرگومان اول آن معرفی می گردد و نیز برای خاتمه یافتن کارش نیاز به تابع Monitor.Exit دارد. بنابراین تعداد Exit ها باید مطابق تعداد Enter ها باشد.
برنامه ابتدا Hell را نمایش داده و سپس بدلیل فراخوانی تابع Wait ، به مدت نامعلومی منتظر یک پیغام که در زمان اجرای برنامه به آن خواهد رسید ، می ماند. بنابراین ما شاهد نمایش Waitabc نخواهیم بود زیرا پس از تابع Wait وارد شده است.
در ادامه ، ترد دوم شروع به اجرای تابع pqr می نماید. تابع Enter از شیء سینکرونایز شده (هم زمانی شده) مشابهی استفاده کرده و اکنون Everyone را نمایش خواهد داد. تابع بعدی که در pqr فراخوانی می شود Monotor.Pulse است. این تابع تمام تردهایی با handle های مشابه را که پس از فراخوانی تابع Wait در حال صبرکردن هستند را راه اندازی می کند. بنابراین ترد موجود در abc شروع به کار می نماید. اگرچه One در pqr برای Someone صبر می کند تا یک Pulse را صادر کند! (مفاهیم بکار گرفته شده در این مثال مطابق فلسفه ی نویسندگان هندی عزیز آن است! : The world has never been able to work with each other. The only global body we have is the UNO. People are mostly loners.)
با استفاده از توابع Pulse و Wait می توان بازی گرگم به هوا (tag) را انجام داد. ترد t کارهایی را انجام خواهد داد و سپس منتظر ترد t1 می شود تا کارش را به پایان برساند. ترد t نمی داند که انجام ترد t1 چقدر طول می کشد. ترد t این مطلب را با صادر شدن یک Pulse از طرف ترد t1 متوجه خواهد شد. در مورد پایان پذیرفتن کار ترد t نیز به همین منوال عمل می گردد و به همین ترتیب. این عملیات ادامه یافته و ممکن است سبب درگیر شدن تردهای دیگر نیز گردد.
تردها از handle g استفاده می نمایند که ناحیه ای synchronized است. تابع Monitor بدین وسیله می تواند آنها را دورهم گرد آورد. اگر این handle را تغییر دهید ، این گروه و پیوند گسسته خواهد شد.
اگر تابع Monitor.Wait(g1) را به Monitor.Wait(g1,0) تغییر دهید ، خروجی به شکل دراماتیکی تغییر خواهد کرد.
سازنده ی کلاس Monitor دارای برچسب private است بنابراین نمی توان از آن شیء ایی ایجاد کرد. اما توابع استاتیک این کلاس قابل فراخوانی هستند.



(امید داشته باشید ، هنوز 37 مثال دیگر باقی مانده است!...)

SSP_Software_team
جمعه 28 آذر 1382, 01:06 صبح
خیلیییییییییییییییییییییی یییی ممنون استاد :wink:

برنامه نویس جوان
شنبه 29 آذر 1382, 15:37 عصر
با سلام

فرض کنید چند ترد با priority های متفاوت در حال Wait بسر می برند آیا با فرمان Pulse تردی که priority آن بیشتر است، زودتر کارش را شروع می کند؟ و آیا تعداد Wait و Pulse باید برابر باشند؟

با تشکر فراوان

Vahid_Nasiri
شنبه 29 آذر 1382, 19:50 عصر
لزومی به یکسان بودن تعدادها نیست و بحث اصلی فقط handle آنها است. همانطور که در متن ذکر شده است :
" این تابع تمام تردهایی با handle های مشابه را که پس از فراخوانی تابع Wait در حال صبرکردن هستند را راه اندازی می کند. "

sql_qassem
یک شنبه 30 آذر 1382, 11:07 صبح
آقا خیلی ممنون ان شاء الله جبران کنیم
SQL 8)

مهدی فهمیده غلامی
شنبه 02 اسفند 1382, 03:43 صبح
اقای نصیری با تشکر از زحمات بی دریغ شما و همکارانتان
یکی از کاربرد های Thread اجرای موازی فرایند ها می باشد
فرض کنید 4 تا editbox داریم من می خوام به محتوای editbox1 هر 1 میلی ثانیه وبه editbox2 هر 2 میلی ثانیه
editbox3 هر 3 میلی ثانیه و بالاخره editbox4 هر 4 میلی ثانیه افزوده شود این زمان بندی با Thread ها چگونه صورت
می گیرد؟؟ و برنامه اش چگونه پیاده سازی می شود؟؟؟

kablayi
جمعه 23 بهمن 1383, 06:33 صبح
:موفق: