Thread چیست؟
موقعیتهایی وجود دارند که در آنها برنامه ما باید حجم بالایی از محاسبات را انجام دهد و یا تعداد درخواستهای زیادی را پاسخگو باشد. اگر این حجم بالا یا تعداد درخواست بالا، عملیاتی مستقل از هم باشند، میتوان این کارها را به موازات هم انجام داد. به طور مثال محاسبهای که بتوان آن را به چند بخش مجزا تبدیل کرد و هر قسمت را مستقل انجام داد یا درخواستهایی که بر روی هم تاثیری نداشته باشند یا به بیانی دیگر وابستگی داده یا منابع نداشته باشند. در چنین شرایطی میتوان از Thread ها استفاده کرد. به این علت که در چنین شرایطی اجرای برنامه در حالت عادی ممکن است بسیار زمانبر شود و یا برنامه هنگ کند و یا وقفههای غیرمتعارف در کارکرد آن رخ دهد؛ برای مدیریت شرایط، مفهومی به نام Thread یا نخ بوجود آمد. در این آموزش مفهوم Thread در سی شارپ را فرا خواهیم گرفت.
در واقع Thread یک رشته از روال اجرای برنامه است که مستقل از روال عادی برنامه کار میکند. برنامه میتواند شامل چندین Thread باشد که در این حالت اصطلاحا برنامه از امکان Multi-Threading بهره میبرد. جالب است بدانید که کل برنامه ما یک Process یا فرآیند یا پردازش است. به بیان دیگر Process نمونهای در حال اجرا از برنامه ماست و هر مسیر اجرایی در این نمونه را می توان به یک Thread تبدیل کرد؛ به شرط آن که این مسیر اجرایی را بتوان به نحوی مستقل از روال عادی برنامه اجرا کرد. اگر هنوز تفاوت بین Process و Thread برای شما قابل درک نیست، تصور کنید هر فانکشن(متد) میتواند یک Thread باشد و کل برنامه میتواند یک Process باشد.
در ابتدای این مطلب نیز اشاره کردیم که یکی از کاربردهای Thread این است که میتوان به کمک آن کارها را به موازات هم انجام داد. در این مطلب که میخواهیم با مفهوم Thread در سی شارپ اشنا شویم، مثال سادهای از اجرای موازی را بررسی خواهیم کرد. مثلا فرض کنید دو حلقه شمارنده در برنامه داریم که یکی از عدد صفر تا ۱۰۰,۰۰۰,۰۰۰ میشمارد و دیگری نیز همین شمارش را با عملیات پیچیدهتر انجام میدهد. در حالت عادی وقتی به حلقه شمارش اول میرسیم، تا شمارش آن انجام نشود، حلقه شمارش دوم آغاز نمیشود. یعنی لحظهای وجود ندارد که هر دو تابع با هم در حال اجرا باشند. در ادامه میخواهیم کاری کنیم تا دو تابع با هم اجرا شوند.
[divider]
نحوه ساخت Thread در سی شارپ
ابتدا یک پروژه جدید از نوع Windows Form Application ایجاد میکنیم و در فرمی که بصورت پیشفرض ساخته شده است،کلیک راست کرده و گزینه View Code را انتخاب میکنیم. صفحهای که نمایش داده میشود، حاوی کدهایی است که در زمان نمایش فرم برنامه اجرا میشوند. در بالای این صفحه، سرآیند زیر را وارد کنید تا در این فرم بتوانیم از قابلیت Threading استفاده کنیم.
using System.Threading;
سپس در داخل کلاس فرم باید دو متد با خروجی void تعریف کنید.
static void my_thread1(){ //codes goes here } static void my_thread2(){ //codes goes here }
در داخل این متدها باید عملیاتی که در ابتدای مطلب عنوان کردیم را کدنویسی کنیم. میتوان از نمونه کد زیر استفاده کرد که دو متد را پیادهسازی کرده است که هر کدام دارای یک حلقه شمارنده هستند که از ۰ تا ۱۰۰,۰۰۰,۰۰۰ را میشمارد. تفاوت این دو در این است که در یکی از متدها اینکار با عملیات پیچیده تر و زمانبر انجام میشود و در دیگری این کار به سرعت انجام میشود. هر دو متد وقتی به اتمام برسند در یک MessageBox نام خود و زمان اتمام کار را اعلام میکنند.
static void my_thread1() { Random rnd = new Random(); for (int i = 0; i < 100000000; i++) { int number = rnd.Next(10000, 20000); i += number; i -= number; } MessageBox.Show("Thread 1: " + DateTime.Now.ToUniversalTime()); } static void my_thread2() { for (int i = 0; i < 100000000; i++) { } MessageBox.Show("Thread 2: " + DateTime.Now.ToUniversalTime()); }
حال باید در فرم برنامه دو button قرار دهیم و نام آن را asyncBtn و syncBtn بگذاریم. سپس بر روی این asyncBtn دو بار کلیک کنید تا وارد بدنه رویداد click آن شوید. در داخل این متد به کمک کد زیر دو Thread ساخته، متدهای my_thread1 و my_thread2 را به آنها نسبت میدهیم و این Thread ها را اجرا میکنیم.
private void asyncBtn_Click(object sender, EventArgs e) { asyncBtn.Enabled = false; syncBtn.Enabled = false; Thread t1 = new Thread(my_thread1); Thread t2 = new Thread(my_thread2); t1.Start(); t2.Start(); asyncBtn.Enabled = true; syncBtn.Enabled = true; }
زمانی که برنامه را اجرا کنید و بر روی دکمه asyncBtn کلیک کنید، خواهید دید که با اینکه t1 زودتر از t2 به اجرا درمیآید، اما دیرتر از آن به پایان میرسد. همچنین هر دوی آنها مدت زمانی به موازات هم اجرا شدند و هر کدام مشغول اجرای کدهای خود هستند. اما t2 چون سادهتر کدنویسی شده بود، در مدت زمان کوتاهتری به اتمام رسیده و t1 که کدی پیچیدهتر دارد، دیرتر به پایان میرسد.
نکته مهم اینکه برنامه اصلی ما که یک فرم است، در زمان اجرای این دو کد دچار هنگ یا کند شدن نشد. حتی با توجه به کد، ما زمان غیرفعال و مجدد فعال شدن دکمهها را احساس نکردیم. برای اثبات این امر میتوانیم همین توابع را بدون اجرا در حالت Thread مستقیما اجرا کنیم تا ببینیم چه بر سر برنامه خواهد آمد. برای این کار کافیست کد زیر را در متد click مربوط به دکمه syncBtn قرار دهیم. سپس برنامه را اجرا کرده و بر روی دکمه syncBtn کلیک کنیم.
private void syncBtn_Click(object sender, EventArgs e) { asyncBtn.Enabled = false; syncBtn.Enabled = false; Form1.my_thread1(); Form1.my_thread2(); asyncBtn.Enabled = true; syncBtn.Enabled = true; }
با کلیک بر روی این دکمه کد بالا اجرا شده و در این حالت اولا مشاهده میکنید که اولین تابعی که اجرا میشود و به پایان میرسد، my_thread1 است و سپس my_thread2 اجرا میشود. تا زمانی که اجرای هر دو تابع به پایان نرسیده باشد، ما غیرفعال بودن دکمهها را مشاهده میکنیم. اگر در فرم اصلی برنامه دکمههای دیگر نیز وجود داشته باشند، در طول زمان اجرای این توابع قابل کلیک کردن نخواهند بود. زیرا برنامه در حالت هنگ به سر میبرد.
برای مطالعه مستندات Thread در سی شارپ میتوانید از لینک زیر استفاده کنید:
[button color=”blue” size=”medium” link=”https://docs.microsoft.com/en-us/dotnet/api/system.threading.thread?view=netframework-4.8″ target=”blank” ]مستندات Thread در سی شارپ[/button]
موفق و پیروز باشید…