Vahid_Nasiri
دوشنبه 01 دی 1382, 00:06 صبح
مثال بیست و پنجم :
using System.Threading;
public class yyy
{
public void abc()
{
Monitor.Enter(this);
Monitor.Enter(this);
for ( int i = 1; i<=3;i++)
{
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
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
در برنامه ی فوق ما دو ترد t و t1 را داریم. فرض کنیم که ترد t ابتدا کارش را آغاز می کند. در این زمان این ترد توسط اولین و دومین Monitor.Enter بدون هیچگونه تاملی شروع به کار می کند. ترد t1 باید برای ورود به اولین Monitor.Enter صبر کند زیرا ترد t ، تابع Enter را دوبار فراخوانی کرده است اما فقط یکبار خارج (Exit) شده است. بنابراین برنامه هرگز خاتمه پیدا نمی کند. اگر تابع Exit دوبار فراخوانی شود ، ترد t1 امکان عمل خواهد یافت.
مثال بیست و ششم :
using System.Threading;
public class yyy
{
public int j=1;
public void abc()
{
Monitor.Enter(j);
for ( int i = 1; i<=3;i++)
{
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(j);
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
}
}
Output
1 2
2 2
3 2
Unhandled Exception: System.Threading.SynchronizationLockException: Exception of type System.Threading.SynchronizationLockException was thrown.
at yyy.abc()
تابع Monitor.Enter تنها اشیاء را بعنوان پارامتر می پذیرد و نه متغیرها را . اگر متغیری در اینجا بکار گرفته شود یک Exception رخ خواهد داد. بنابراین انواع عددی و یا اشیاء null را نمی توان در اینجا بعنوان پارامتر ورودی مورد استفاده قرار داد.
مثال بیست و هفتم :
using System.Threading;
public class xxx
{
}
public class ttt
{
}
public class yyy
{
static int p = 1;
xxx x = new xxx();
ttt t = new ttt();
public void abc()
{
if ( p == 1)
{
Monitor.Enter(x);
p++;
}
else
Monitor.Enter(t);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(x);
}
}
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
1 3
2 2
2 3
3 2
3 3
از تابع Enter باید با دقت و احتیاط استفاده نمود. در مثال فوق متغیر P در ابتدا دارای مقدار 1 است. اولین ترد یعنی t ، مقدار P را مساوی یک مشاهده می کند. بنابراین عبارت if صحیح خواهد بود. در ادامه از تابع Monitor.Enter با پارامتر x استفاده می شود. این ترد با کمک تابع Sleep به خواب می رود ، در حالیکه ترد دوم (t1) اجرای تابع abc را شروع می کند و در اینجا آن مقدار متغیر P را معادل 2 خواهد دید. در ادامه این ترد با تابع Monitor.Enter با پارامتر t ملاقات خواهد کرد. شیءایی که به این تابع Enter بعنوان پارامتر فرستاده شده است کاملا متفاوت است با شیء t ایی که برای ترد قبلی مورد استفاده قرار گرفت. بنابراین هر دو ترد تابع Enter را اجرا خواهند کرد که سبب از بین رفتن هدف استفاده ی اولیه از تابع Enter است. برای اینکه تابع Enter همانطور که انتظار می رود کار کند باید پارامتر مشابهی را بکار گرفت.
از مفهوم Lock به صورت معمول برای توضیح دادن کلاس Monitor استفاده می شود. هنگامیکه یک ترد Lock می شود سایر تردها باید صبر کنند تا این قفل گشوده شود.
مثال بیست و هشتم:
using System.Threading;
public class yyy
{
public void abc()
{
bool b = Monitor.TryEnter(this);
System.Console.WriteLine(b);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
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
True
False
1 2
1 3
2 2
2 3
3 2
3 3
تابع TryEnter مشابه تابع Enter است اما سبب block شدن نخواهد گردید. اگر ترد به صورت موفقیت آمیزی بتواند enter کند ، این تابع true بر می گرداند. این چیزی است که برای بار اول اتفاق می افتد. اما در زمانی که ترد دوم می خواهد وارد شود ، این تابع false بر می گرداند.
مثال بیست و نهم :
using System.Threading;
public class yyy
{
public void abc()
{
bool b = Monitor.TryEnter(this,1000);
System.Console.WriteLine(b);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
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
True
False
1 2
1 3
2 2
2 3
3 2
3 3
تابع TryEnter اعداد را نیز بعنوان پارامتر می پذیرد و این اعداد بیانگر مدت زمانی که TryEnter باید منتظر بماند و یا بلاک کند ، می باشد. در مثال فوق ، timeout از زمانی که ترد t در تابع صرف کرده است ، تجاوز نموده است. بنابراین ما مقدار false را دریافت خواهیم کرد. اگر زمان پارامتر افزایش داده شود ، خروجی true می گردد. اگر مقدار پارامتر نامتناهی شود ، رفتار تابع TryEnter و تابع Enter یکی خواهند گردید.
using System.Threading;
public class yyy
{
public void abc()
{
Monitor.Enter(this);
Monitor.Enter(this);
for ( int i = 1; i<=3;i++)
{
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
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
در برنامه ی فوق ما دو ترد t و t1 را داریم. فرض کنیم که ترد t ابتدا کارش را آغاز می کند. در این زمان این ترد توسط اولین و دومین Monitor.Enter بدون هیچگونه تاملی شروع به کار می کند. ترد t1 باید برای ورود به اولین Monitor.Enter صبر کند زیرا ترد t ، تابع Enter را دوبار فراخوانی کرده است اما فقط یکبار خارج (Exit) شده است. بنابراین برنامه هرگز خاتمه پیدا نمی کند. اگر تابع Exit دوبار فراخوانی شود ، ترد t1 امکان عمل خواهد یافت.
مثال بیست و ششم :
using System.Threading;
public class yyy
{
public int j=1;
public void abc()
{
Monitor.Enter(j);
for ( int i = 1; i<=3;i++)
{
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(j);
}
}
public class zzz
{
public static void Main()
{
yyy a = new yyy();
Thread t = new Thread(new ThreadStart(a.abc));
t.Start();
}
}
Output
1 2
2 2
3 2
Unhandled Exception: System.Threading.SynchronizationLockException: Exception of type System.Threading.SynchronizationLockException was thrown.
at yyy.abc()
تابع Monitor.Enter تنها اشیاء را بعنوان پارامتر می پذیرد و نه متغیرها را . اگر متغیری در اینجا بکار گرفته شود یک Exception رخ خواهد داد. بنابراین انواع عددی و یا اشیاء null را نمی توان در اینجا بعنوان پارامتر ورودی مورد استفاده قرار داد.
مثال بیست و هفتم :
using System.Threading;
public class xxx
{
}
public class ttt
{
}
public class yyy
{
static int p = 1;
xxx x = new xxx();
ttt t = new ttt();
public void abc()
{
if ( p == 1)
{
Monitor.Enter(x);
p++;
}
else
Monitor.Enter(t);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
Monitor.Exit(x);
}
}
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
1 3
2 2
2 3
3 2
3 3
از تابع Enter باید با دقت و احتیاط استفاده نمود. در مثال فوق متغیر P در ابتدا دارای مقدار 1 است. اولین ترد یعنی t ، مقدار P را مساوی یک مشاهده می کند. بنابراین عبارت if صحیح خواهد بود. در ادامه از تابع Monitor.Enter با پارامتر x استفاده می شود. این ترد با کمک تابع Sleep به خواب می رود ، در حالیکه ترد دوم (t1) اجرای تابع abc را شروع می کند و در اینجا آن مقدار متغیر P را معادل 2 خواهد دید. در ادامه این ترد با تابع Monitor.Enter با پارامتر t ملاقات خواهد کرد. شیءایی که به این تابع Enter بعنوان پارامتر فرستاده شده است کاملا متفاوت است با شیء t ایی که برای ترد قبلی مورد استفاده قرار گرفت. بنابراین هر دو ترد تابع Enter را اجرا خواهند کرد که سبب از بین رفتن هدف استفاده ی اولیه از تابع Enter است. برای اینکه تابع Enter همانطور که انتظار می رود کار کند باید پارامتر مشابهی را بکار گرفت.
از مفهوم Lock به صورت معمول برای توضیح دادن کلاس Monitor استفاده می شود. هنگامیکه یک ترد Lock می شود سایر تردها باید صبر کنند تا این قفل گشوده شود.
مثال بیست و هشتم:
using System.Threading;
public class yyy
{
public void abc()
{
bool b = Monitor.TryEnter(this);
System.Console.WriteLine(b);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
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
True
False
1 2
1 3
2 2
2 3
3 2
3 3
تابع TryEnter مشابه تابع Enter است اما سبب block شدن نخواهد گردید. اگر ترد به صورت موفقیت آمیزی بتواند enter کند ، این تابع true بر می گرداند. این چیزی است که برای بار اول اتفاق می افتد. اما در زمانی که ترد دوم می خواهد وارد شود ، این تابع false بر می گرداند.
مثال بیست و نهم :
using System.Threading;
public class yyy
{
public void abc()
{
bool b = Monitor.TryEnter(this,1000);
System.Console.WriteLine(b);
for ( int i = 1; i<=3;i++)
{
Thread.Sleep(1000);
System.Console.WriteLine(
i + " " + Thread.CurrentThread.GetHashCode() + " ");
}
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
True
False
1 2
1 3
2 2
2 3
3 2
3 3
تابع TryEnter اعداد را نیز بعنوان پارامتر می پذیرد و این اعداد بیانگر مدت زمانی که TryEnter باید منتظر بماند و یا بلاک کند ، می باشد. در مثال فوق ، timeout از زمانی که ترد t در تابع صرف کرده است ، تجاوز نموده است. بنابراین ما مقدار false را دریافت خواهیم کرد. اگر زمان پارامتر افزایش داده شود ، خروجی true می گردد. اگر مقدار پارامتر نامتناهی شود ، رفتار تابع TryEnter و تابع Enter یکی خواهند گردید.