PDA

View Full Version : مقاله: MultiThreading چیست؟



Mani_rf
سه شنبه 03 آذر 1388, 20:16 عصر
مقدمه -
گاهی اوقات ممکن است که شما بخواهید برنامه شما دو یا چند عمل را به طور همزمان انجام دهد و یا اینکه نیاز به انجام عملیاتی که مدت زمان زیادی به طول می انجامد و یا زمان انجام آن معلوم نیست ، باشد ، بدون اینکه برنامه شما از دسترس کاربر خارج شود و به اصطلاح برنامه شما تا پایان یافتن عملیات قفل کند و همچنین کاربر بتواند عملیات را متوقف/معلق/شروع دوباره نماید . در چنین موقعیتی نیاز به MultiThreading حس میشود . به فرض مثال کد زیر را در نظر بگیرید :
کد:
For i As Integer = 0 To 10000000
For i2 As Integer = 0 To 100
'Do Nothing
Next
Next
هنگامی که این عملیات شروع میشود ، کاربر توانایی کار با برنامه تا پایان یافتن آن را نخواهد داشت .

Thread چیست؟
Thread نامی برای جریان اجرای یک عملیات خاص میباشد و هنگامی که برنامه شما دارای چند Thread میباشد بدان معناست که قسمت های مختلفی از کد برنامه شما به طور همزمان در حال اجرا شدن میباشند . در حقیقت کامپیوتر زمان پردازش یک عملیات را به قسمت(slice) های مختلفی تقسیم میکند و هنگامی که شما یک Thread جدید را آغاز میکنید کامپیوتر قسمتی از زمان را به آن اختصاص میدهد . لازم به ذکر است که برنامه شما از ابتدا دارای یک Thread اصلی (Main Thread) برای اجرا کد مربوط به آن میباشد .

کار خود با Thread ها را آغاز مینماییم :
میخواهیم برنامه ای بنویسیم که تا یک عدد معین عملیات شمارش را انجام دهد .
1 – یک پروزه Windows Apploication به نام MutiThreading Sample ایجاد نمایید .
2 – یک Button به نام btnStart و یک TextBox به نام txtMAX به فرم اضافه نمایید .
3 – یک کلاس به نام clsCounter به پروژه اضافه کرده و کد زیر را در داخل آن قرارهید :
کد:
Public Class clsCounter
Public MAX As Integer
Public Event CountingFinished(ByVal Number As Integer)
Sub StartCounting()
Dim intTotal As Integer
For i As Integer = 0 To MAX
intTotal += 1
Next
RaiseEvent CountingFinished(intTotal)
End Sub
End Class
توضیحات در مورد کد فوق :
• وظیفه این کلاس شمردن از 1 تا مقدار MAX میباشد .
• رویدادی با نام CountingFinished تعریف کردیم که هنگامی که عملیات شمارش به پایان برسد اتفاق می افتد .
• متد StartCounting از 1 تا مقدار intMax را شماره کرده و در هر بار اجرای حلقه یک واحد به مقدار متغیر intTotal اضافه میشود که در نهایت مساوی با مقدار MAXخواهد بود .
• پس از پایان شمارش رویداد CountingFinished را همراه با پاس کردن متغیر intTotal به آن اجرا مینماییم .

حال ما باید در هنگامی که دکمه کلیک میشود یک Thread جدید ایجاد کرده و سپس متد StartCounting کلاس clsCounter را اجرا کرده و رخدادن رویداد CountingFinished را کنترل نماییم . در زمانی که عملیات شمارش انجام میشود ما میتوانیم رابط کاربری را کنترل کرده و کاربر توانایی کار با برنامه را دارد .

حال کد زیر را به پروژه خود اضافه نمایید :
کد:
Sub CountingFinishedEventHandler(ByVal N As Integer)
System.Windows.Forms.MessageBox.Show("Counting Finished!")
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Dim CounterClass As New clsCounter
Dim CountingThread As New Threading.Thread(AddressOf CounterClass.StartCounting)
CounterClass.MAX = Val(txtMax.Text)
AddHandler CounterClass.CountingFinished, AddressOf CountingFinishedEventHandler
CountingThread.Start()
End Sub
توضیحات در مورد کد فوق :
•ابتدا یک پروسیجر برای کتترل رویداد CountingFinished مربوط به کلاس Counter ایجاد مینماییم . هنگامی که رویداد اتفاق بیافتد(عملیات شمارش به پایان برسد) ، پیغامی مبنی بر پایان یافتن عملیات به کاربر نشان داده خواهد شد .
• در رویداد Click شی ء btnStart ، ابتدا یک نمونه از کلاس CounterClass ایجاد مینماییم .
• سپس برای ایجاد شی ء Thread ، آدرس متذ clsCounter.StartCounting را به سازنده کلاس Thread پاس مینماییم به طوری که متد clsCounter.StartCounting را بعد از آوردن کلمه کلیدی addressof ، می آوریم .
• بعد ، توسط کلمه کلیدی Addhandle ، کنترل کننده رویداد که CountingFinishedEventHandler نام دارد را به رویداد clsCounter.CountingFinished متصل مینماییم .
• در آخر نیز توسط متد Start مربوط به شیء CountingThread ، عملیات را آغاز مینماییم .

برخی متدهای دیگر مربوط به شی ء Thread :
Suspend و Resume : در حالی که یک Thread در حال اجراست ، توسط متد Suspend میتوانید آن را معلق کنید که منجر به متوقف شدن آن تا زمانی که متد Resume اجرا شود ، خواهد گردید .
Abort : Thread را متوقف میکند .
Sleep : توسط این متد میتوانید اجرای Thread را برای پاره ای از زمان (برحسب میلی ثانیه) به حالت تعلیق دربیاورید .


اولویت بندی Thread ها :
شما کنترل بیشتری بر روی Threadها دارید و میتوانید مقدار زمانی که هر Thread نسبت به دیگر Thread ها دریافت میکند را از طریق خاصیت Priority تنظیم نمایید . این خاصیت توسط یکی از ثابت های شمارشی زیر که عضوی از ThreadPriority میباشد تنظیم میشود :
ThreadPriority.AboveNormal : اولویت بالاتری به Thread میدهد .
ThreadPriority.LowerPriority : اولویت پایین تری به Thread میدهد .
ThreadPriority.HighestPriority : بالاترین اولویت را به Thread میدهد .
ThreadPriority.LowestPriority : پایین ترین اولویت را به Thread میدهد .
ThreadPriority.Normal : تولویت نرمال را به Thread میدهد .

پیدا کردن وضعیت Thread :
وضعیت یک Thread را میتوانیم به وسیله خاصیت ThreadState به دست بیاوریم که به وسیله یکی از ثابتهای شمارشی System.Threading.ThreadState معین میگردد .
System.Threading.ThreadState.Initialized : بیان میکند که Thread مقداردهی اولیه شده اما هنوز شروع نگردیده است .>System.Threading.ThreadState.Ready : Thread آماده است .
System.Threading.ThreadState.Running : بیان میکند که Thread در حال اجرا است .
System.Threading.ThreadState.Standbye : بیان میکند که Thread در حالت آماده به کاراست .
System.Threading.ThreadState.Initialized :بیان میکند که Thread به پایان رسیده است .
System.Threading.ThreadState.Transition : بیان میکند که Thread بین دو وضعیت بوده و در حالت انتقال از وضعیتی به وضعیت دیگر است .
System.Threading.ThreadState.Unknown : بیا میکند که وضعیت Thread معلوم نیست .
System.Threading.ThreadState.Wait : بیان میکند که Thread در حالت انتظار است .

(این نوشت مقاله ای بود (http://barnamenevis.org/forum/showpost.php?p=97132&postcount=1) بسیار زیبا و کارا از دوست عزیزمان علیرضا مداح (http://barnamenevis.org/forum/member.php?u=4649))

mortezamhd
یک شنبه 13 دی 1388, 01:03 صبح
سلام

من از Thread هیچ اطلاعاتی نداشتم ولی با این توضیح آقای مداح تازه فهمیدم که چی به چیه

البته از دوست خوبم آقای رضائی هم ممنونم که تاپیک سال 84 رو دوباره یاداوری کردن و...

یک سوال چرا وقتی مثلا من یک بار که یک شماره ای رو دادم و شمارش شد و دوباره که یک شماره دیگه ای دادم به تعداد دفعات اجرای Thread شمارش میشه ( مثلا من یک عدد وارد کردمو شمارش تمام شد وپیغام Counting Finished هم صادر شد بعد که یک بار دیگه که روباتن کلیک کردم Thread دو بار اجرا میشه و دو بار پیغام Counting Finished صادر میشه و باز دوباره که عددو میخوام شمارش کنم سه بار اجرامیشه و بعد چهار بار و ..... بار اجرا میشه ):لبخند:

خوب حالا این مشکل از کجاست ؟ برای رفع این مشکل باید چکار کرد ؟

منتظر راهنماییتون هستم . مرسی

hossein-khoshseyar
شنبه 03 بهمن 1388, 14:45 عصر
سلام

من از Thread هیچ اطلاعاتی نداشتم ولی با این توضیح آقای مداح تازه فهمیدم که چی به چیه

البته از دوست خوبم آقای رضائی هم ممنونم که تاپیک سال 84 رو دوباره یاداوری کردن و...

یک سوال چرا وقتی مثلا من یک بار که یک شماره ای رو دادم و شمارش شد و دوباره که یک شماره دیگه ای دادم به تعداد دفعات اجرای Thread شمارش میشه ( مثلا من یک عدد وارد کردمو شمارش تمام شد وپیغام Counting Finished هم صادر شد بعد که یک بار دیگه که روباتن کلیک کردم Thread دو بار اجرا میشه و دو بار پیغام Counting Finished صادر میشه و باز دوباره که عددو میخوام شمارش کنم سه بار اجرامیشه و بعد چهار بار و ..... بار اجرا میشه ):لبخند:

خوب حالا این مشکل از کجاست ؟ برای رفع این مشکل باید چکار کرد ؟

منتظر راهنماییتون هستم . مرسی



خوب دوست عزیز شما هر بار که ترد رو اجرا می کنی کل اون تابعی که ترد به اون اشاره می کنه از ابتدا تا انتها اجرا می شه این که مشکلی نیست
شما بگو می خوای چیکار کنی تا راهنماییتون کنم

dr_jacky_2005
شنبه 01 آبان 1389, 15:06 عصر
من یک فرم لودینگ دارم که تووش پروگرس بار است.
یک فرم هم دارم که تووش درخت است و با تابعی پر از دیتا میشود.
اینگونه عمل کرده ام:
این رو نوشتم توو فرم که درخت تووشه که بتونم فرم لودینگ رو شو کنم:

LoadingForm frmp;

اینم توو لود فرمه که تووش درخته:

frmp = new LoadingForm();
frmp.Show();
this.Cursor = Cursors.WaitCursor;
backgroundWorker1.RunWorkerAsync();

اینم توو دوو ورک بکگرادنورکر:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int returnedRecordCount = 100;
for (int i = 1; i <= returnedRecordCount; i++)
{
if (this.backgroundWorker1.CancellationPending)
{
e.Cancel = true;
return;
}
this.backgroundWorker1.ReportProgress(i);

System.Threading.Thread.Sleep(20);
}
if (TrvCoding.InvokeRequired)
{

TrvCoding.Invoke(new MethodInvoker(delegate { TvfCoding.LoadTreeFullDate(); }));

//TvfCoding.LoadTreeFullDate();
}
}

اینم توو کامپلیت:

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Cursor = Cursors.Default;
frmp.Close();
//frmp.Hide();
}

اینم وقتی که فرمه که تووش درخته،دارخ بسته میشه:

file:///C:/DOCUME%7E1/GHEZEL%7E1/LOCALS%7E1/Temp/moz-screenshot.pngprivate void HesabStateWorkForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync();
}

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage <= this.frmp.progressBar1.Maximum)
{
this.frmp.progressBar1.Value = e.ProgressPercentage;
}
}

اینم توو لود فرم لودینگ:

progressBar1.Value = 0;
for (int x = 0; x < 100; x++)
{
if (progressBar1.Value < progressBar1.Maximum)
{
//System.Threading.Thread.Sleep(10);
progressBar1.Value++;
}
}

اینا هم نکته ها:
دو تا پروپرتی توانایی کنسل کردن و گزارش دادن true است.
و
فرم لودینگ هم TopMost = true

سوالات:
الآن همه چی میاد،عالی...
ولی
اول لودینگ میاد،بعد پر شدن درخت شروع میشه!

من میخوام هم زمان باشن!


دومین چیزی که میخوام اینه که پر شدن لودینگ متناسب باشه به مقداری که لازمه.

اگر مورد بالا نشد،میخوام که پروگرس بار بلوکی نباشد،marquee باشد.(style)

hossein-khoshseyar
یک شنبه 02 آبان 1389, 13:08 عصر
فرق بین Thread و BackGroundWorker چیه؟

BackGroundWorker چیزی نیست جز همون تردی که بالا معرفی شد
فقط BackGroundWorker اومده استفاده از ترد رو ساده تر کرده
یعنی نیاز به تعریف و آدرس دادن اون رو حذف کرده وخودش اون کار رو انجام میده
شما برای هر ترد می تونید یک BackGroundWorker بیارید توی فرم وازش استفاده کنید و دیگه نیازی نیست که توی متن برنامه ازش استفاد هکنید و بهش آدرس بدیدو....
درواقع ترد رو به شکل یک کامپوننت در اختیارتون هست

dr_jacky_2005
یک شنبه 02 آبان 1389, 14:24 عصر
BackGroundWorker چیزی نیست جز همون تردی که بالا معرفی شد
فقط BackGroundWorker اومده استفاده از ترد رو ساده تر کرده
یعنی نیاز به تعریف و آدرس دادن اون رو حذف کرده وخودش اون کار رو انجام میده
شما برای هر ترد می تونید یک BackGroundWorker بیارید توی فرم وازش استفاده کنید و دیگه نیازی نیست که توی متن برنامه ازش استفاد هکنید و بهش آدرس بدیدو....
درواقع ترد رو به شکل یک کامپوننت در اختیارتون هست

مرسی ولی این توضیحات مشکل منو حل نمیکنه:چشمک:

Shahram_Shobeiri
دوشنبه 03 آبان 1389, 12:10 عصر
گاهی اوقات لازم است که از ترد جاری به یک ترد دیگر دسترسی پیدا کنیم. مثلاً در یک ترد فرعی هستیم و می خواهیم متن فرم اصلی را تغییر بدهیم.


Private test_thread As New System.Threading.Thread(AddressOf set_form_text)

Private Sub set_form_text()
Me.Text = "Hello World!"
Threading.Thread.Sleep(50)
End Sub
پس از شروع ترد فوق


test_thread.Start()
با خطای زیر مواجه خواهیم شد.
Cross-thread operation not valid: Control 'Form_main' accessed from a thread other than the thread it was created on.
متن خطا کاملاً گویا است : ‌کنترل فلان از تردی غیر از ترد خودش دستکاری شده
حالا این خطا رو چطور رفع کنیم؟
برای رفع این خطا باید از یک نماینده بین این دو ترد استفاده کنیم. روش کار بصورت زیر است:


Private test_thread As New System.Threading.Thread(AddressOf set_form_text)

Private Sub set_form_text()
set_form_text_thread_safe()
Threading.Thread.Sleep(50)
End Sub

Delegate Sub set_form_text_delegate()

Private Sub set_form_text_thread_safe()
If Me.InvokeRequired Then
Dim my_deledgate As New set_form_text_delegate(AddressOf set_form_text_thread_safe)
Me.Invoke(my_deledgate)
Else
Me.Text = "Hello World!"
End If
End Sub
یک توضیح مختصر در مورد کد فوق:
در کد بالا یک delegate و یک sub جدید تعریف کردیم. کار این ساب جدید این است که مقدار متن فرم اصلی رو به صورت غیر مستقیم تغییر میدهد. در خط اول این ساب بررسی می کنیم که آیا لازم است فرم اصلی را به ترد جاری احضار کنیم یا به عبارت بهتر آیا فرم اصلی خارج از ترد جاری است؟ اگر نبود (else) که مقدار متن فرم اصلی را ست می کنیم. ولی اگر (خارج از ترد جاری) بود، آنگاه می بایست نماینده ای از ساب جاری ساخته و این نماینده را در ترد اصلی احضار کنیم. از این پس ساب جای نماینده ای در ترد اصلی خواهد داشت و به این ترتیب دیگر شرط برقرار نشده و همواره قسمت else اجرا خواهد شد.

Mani_rf
شنبه 12 بهمن 1392, 23:06 عصر
توی تاپیک های قدیمی این تاپیک را دیدم، احساس کردم که کاربردیه برای همین آوردمش بالا که دوستانی که تمایل دارن ازش استفاده کنن.
امیدوارم کارا باشه.