سلام بر گلپسر عزیز .
خیلی سئوال خوبی پرسیدی . اما اول بذار قبل از جواب دادن به سئوالاتت بصورت مجزا ، یه توضیح مفصل از عملکرد پردازنده ها و رابطه اش با نرم افزارها را بگم چون هر کدوم از سئوالاتت به اون یکی ربط داره و جوابش بصورت یک سریال هه . شاید این توضیح خودش یه پست کامل بشه . بعد میریم سر وقت جواب دادن به دونه دونه ی سئوالاتت . چون اینترنت بین الملل الان کار نمیکنه ، نمیشه بعضی از لینک های مفید را شاید داد .
اول اینکه توی پردازنده ، چیزی بنام ترد یا نخ یا همون رشته نداریم .
ترد یا نخ یا رشته ، یه چیز نرم افزاری هست و سخت افزاری نیست . (از این به بعد ، هر جا گفتم نخ ، منظورم همون ترد یا رشته هست) . نخ هم چیزی نیست جز اجرای تابع یا متد در برنامه نویسی (که با متدها آشنا هستی حدودا) (البته یه تفاوت خیلی کوچیک داره) . در واقع ، هیچ متدی اجرا نمیشه ، مگر اینکه درون یک نخ اجرا بشه . در واقع ، تا زمانی که پروسه ای در حال اجراست ، یعنی حداقل ، متد main اش در حال اجرا هست (متد main را در قسمت 61 آموزش سی شارپ گفتم) بنابراین متد main (همونطور که در بالا گفتم) ، درون یک نخ بنام نخ اصلی اجرا میشه . بنابراین ، هر پروسه ، حداقل یک نخ داره که بهش نخ اصلی میگن .
اما یه برنامه (یا یک پروسه) ، میتونه بیشتر از یک نخ داشته باشه . برنامه نویس میتونه برای برنامه و پروسه اش ، تعداد بی نهایت نخ درست کنه (البته منظورم از بی نهایت ، به معنای واقعی کلمه و اجرا و ایجاد یک دفعه ی تعداد بسیار زیادی از نخ ها نیست چون کامپیوترها محدودیت منابع و حافظه دارن) . همانطور که شما میتونی تعداد بی نهایت متد را اجرا کنی چون در هر تعداد اجرای متدها که مشکلی نداری (البته این هم مثل اون ، نه به معنای واقعی کلمه و اجرای متدهای تو در تو بصورت بسیار زیاد نیست چون مثل همون قضیه ، هر وقت متدی اجرا میشه ، نیاز به حافظه ی جدیدی داره و کامپیوتر هم منابع و حافظه ی محدودی دارن و ارور stack overflow را میده که در قسمت 25 و 26 در آموزش سی شارپ ، توابع بازگشتی و اجرای تو در توی متدها ، گفته شد) .
دقت کن منظورم از اینکه گفتم "نخ هم چیزی نیست جز اجرای تابع یا متد" ، این نیست که هر وقت متدی را فراخونی کردی ، پس یعنی نخ جدیدی را ایجاد و فراخونی کردی . نه . فرایند ساخت و اجرای نخ ، متفاوت هه . باید از کلاس Thread یا ThreadPool یا Task یا BackgroundWorker (یا کلاس های بسیار زیادی که در این رابطه وجود دارن) ، شی بسازید و متدی را به این کلاس معرفی کنید و توسط اعضای این کلاس ، اون متد را فراخونی کنید تا در نخ جدیدی ، متدتون فراخونی بشه .
همونطور هم که هر وقت و در هر رویدادی که خواستی ، یه متد (یا متدهایی بصورت پشت سر هم یا حتی تو در تو) را فراخونی کنی و بعد اجرای اون متد (یا متدها) تمام میشه و در زمان و رویداد دیگه ای ، میتونی یک متد (یا متدهایی بصورت پشت سر هم یا حتی تو در تو) فراخونی کنی ؛ نخ ها هم به همین صورت ، هر وقت برنامه نویس خواست ، یه نخ (یا نخ های متوالی) را میتونه اجراشون کنه و بعد اجرای اون نخ (یا نخ ها) تموم میشه و در زمان و رویداد دیگه ای ، میشه باز یک نخ (یا نخ هایی را بصورت متوالی) اجرا کرد .
بنابراین اجرای نخ در یک پروسه (برنامه) ، میتونه مدام در حال تغییر باشه چون همونطور که گفتم ، ممکنه نخ هایی را در یک زمان (و رویداد خاصی) ایجاد و اجرا کرده باشن و اجرای نخ (و بنابراین خود اون نخ) تموم بشه (یا حتی برنامه نویس اون نخ را متوقف کنه تا اجرای اون نخ تموم نشه) و چند مدت بعد ، دوباره در یک رویداد (یا متد دیگه) ، نخ (یا نخ های جدید) دیگه ای را ایجاد و اجرا کرده باشن . بنابراین مثلا یه پروسه ، میتونه حتی ده ها نخ را در یک زمان خاص داشته باشه (مثلا 100 تا نخ) اما در ثانیه ی بعد ، همه ی اجرای اون نخ ها تموم شده باشه و فقط یک نخ براش مونده باشه اما باز دوباره در لحظه ی بعد ، مثلا 15 نخ را اجرا کنه اما باز دوباره در لحظه ی بعد ، مثلا اجرای 10 نخ اش تمام شده باشه و بنابراین 5 نخ اش مونده باشه . میخوام بگم که این طور نیست که حتما یک پروسه مثلا 3 نخ را از اول اجرای برنامه اش که اجرا کرد ، تا آخرِ اجرایِ برنامه و پروسه اش ثابت باشه و دیگه اجرای نخ اش تموم نشه یا دیگه اضافه نشه . هر لحظه ممکنه اجراشون تموم بشه (و بنابراین نخ از بین بره) و هر لحظه هم ممکنه نخ جدید بسازه و اجرا کنه . هر چند ، میتونه هم در مواقعی نخ را متوقف کنه و کاری کنه تا آخر اجرای پروسه ، نخ های دیگه ای که ساخت ، تمام نشه و در حال اجرا باشه .
حالا چرا اصلا نخ دیگه و نخ جدید ساخته میشه (و چرا در همون نخ اصلی همه ی کارها انجام نمیشه) ، بخاطر اینه که گاها پیش میاد یه نخ ، باید منتظر دریافت ورودی از کاربر باشه (این ورودی ، میتونه انتخاب گزینه ی yes و no توسط موس از کاربر باشه ، فشردن کلیدی در کیبرد باشه و ...) اما کارهای دیگه ای هست که هیچ ربطی به این کارها (یی که بعد از دریافت ورودی از کاربر که باید انجام بشه) ، ندارن . بنابراین دو کار کاملا متفاوت میتونن کاملا جدای از هم انجام بشن . بنابراین دو نخ مجزا ، برای دو کارِ متفاوت ایجاد میکنن و هر کدوم را در نخ ای انجام میدن . مثلا وقتی که از کاربر یک سئوالی برای حذف فایلی میپرسیم (در صورت جواب مثبت ، میخوایم فایل حذف بشه) ، و همون زمان هم نیاز داریم مثلا یک عکس و تصویری را در فایل png در هارد یا در رم ذخیره کنیم . چون این دو کار ، هیچ ربطی به هم نداره ، میتونیم در دو نخ متفاوت اجراشون کنیم . بنابراین هر وقت ، کاری وجود داشت که هیچ ربطی به کار قبلی نداشت ، این کار جدید را در نخ جدید انجام میدن .
اما اگه (در همین قضیه ی حذف فایل) نخ جدیدی اجرا نمیکردیم و فقط در یک نخ اجرا میکردیم ، چی میشد؟
چون کدها در هر نخی که اجرا میشن ، به ترتیب اجرا میشن و کدی اجرا نمیشه مگر اینکه کد قبلی اش اجرا شده باشه ، بنابراین وقتی که کد حذف فایل را نوشتیم و بعدش کد ذخیره ی تصویر در هارد را نوشتیم ، در این صورت ، اول وقتی پیام MessageBox.Show را که دادیم و از کاربر پرسیدیم که میخوای فلان فایل را حذف کنی یا نه ، این متد ، کد را در همون نخ متوقف میکنه (هر وقت کد ، در نخی متوقف بشه ، ادامه ی کدها در همون نخ اجرا نمیشه . واسه ی همینه که تا به MessageBox.Show جوابی ندی ، کدهای خط بعدش که نوشتی ، اجرا نمیشه) تا کاربر بهش جوابی بده . این جواب ، ممکنه مثلا 10 دقیقه طول بکشه . یعنی ، مثلا کاربر ، 10 دقیقه ی بعدش بیاد جواب بده . بنابراین کدِ بعد از MessageBox.Show هم بعد از 10 دقیقه تازه اجرا میشه . یعنی بعد از 10 دقیقه ، تازه اون عکس ذخیره میشه در حالی که این کارمون (ذخیره ی عکس)، هیچ ربطی به جواب کاربر نداشت و میتونستیم همون موقعی که به کاربر پیام میدیم که میخواد فایلش حذف بشه یا نه ، اون عکس را هم ذخیره کنیم و کارهای مربوط به اون عکس را هم برسیم .
حالا ، در این مثال ، تعداد کارهایی که میتونست مجزای از همدیگه انجام بشه ، 2 تا بود و بنابراین یه نخ جدید میخواست (چون یه نخ اصلی که همیشه در حال اجرا هست و میتونیم یه کار را توش انجام بدیم . هر چند باز هم برای دو تا کار ، علاوه بر نخ اصلی ، میتونیم دو تا نخ جدید دیگه هم بسازیم که با نخ اصلی ، 3 نخ در حال اجرا در اون موقع میشه) اما ممکنه در یک موقع ، 10 تا کار جدیدِ مجزای از هم برامون پیش بیاد بنابراین میتونیم 9 نخ جدید در همون موقع اجرا کنیم . بسته به تعداد کارهای مجزامون داره .
اینکه چه پروسه ای ، در حال حاضر داره چند تا نخ را اجرا میکنه را میتونی از پنجره ی Resource Monitor (قسمت یا سربرگ CPU و ستون Threads) ببینی . که معمولا هم در حال تغییر هستند ، مخصوصا اگه برنامه ای فعال و در حال کار کردن باشه (البته باز هم بستگی داره به برنامه نویس اون برنامه که نخی درست کرده باشه یا نه و اون نخ را متوقف کرده باشه یا نه و چه مدت زمانی اجرای اون طول بکشه) (البته اگه برنامه ی سی شارپ را چک میکنی ، احتمالا خود clr ، چند تا نخ دیگه هم برای برنامه های دات نت میسازه و به همین دلیل هست که برنامه های دات نت ، زمان اجرا ، چندین نخ دارن بدون اینکه برنامه نویس ، خودش نخ فرعی ای را اجرا کرده باشه (دقیق نمیدونم . حالا در این باره ی clr ، از کسایی که مطلع هستند سئوال کن)) . اگه هم میخوای ببینی مجموع برنامه های در حال اجرا در کامپیوترت ، چند نخ در حال اجرا دارند ، در Task Manager ، در سربرگ Performance برو و در قسمت پایینی ، نوشته ی Threads را ببین (معمولا وقتی کامپیوتر روشن میشه و بدون هیچ نرم افزار اضافی ای ، بیش از 1000 نخ هست که مدام در حال تغییر هه) .
نخ ها را سیستم عامل کنترل میکنه . سیستم عامل ، از بین نخ های زیادی که پروسه های مختلف برای پردازش به سیستم عامل درخواست دادن ، نخ های مورد نظرش را انتخاب میکنه و برای پردازنده برای پردازش کردن میفرسته . اینکه مثلا زمانی که 23 تا نرم افزار (پروسه) رو فورا بصورت همزمان اجرا کنیم (پس از اونجایی که هر پروسه ، حداقل یک نخ دارن ، پس 23 نخ مجزا ، بصورت همزمان اجرا میشن) (البته ، کلمه ی فورا که گفتم ، شاید عبارت دقیقی نباشه که بعدها مفصل تر توضیح بدم ، متوجه میشی) ، و پردازنده ی ما هم 6 هسته ای با 12 هسته ی منطقی (یا همون 12 نخ که گفتم که اصطلاح نخ برای پردازنده درست نیست) باشه ، پس ، حداکثر 12 نخ (در اینجا ، پروسه) را میتونه همزمان اجرا کنه بنابراین 11 نخ دیگه (در اینجا ، 11 پروسه ی دیگه) باقی میمونه اما اینکه پردازش کدوم نخ ها از اون 12 نخ ، مهمتر از اون 11 نخ هستن که زودتر از اون 11 نخِ دیگه پردازش بشن را سیستم عامل تعیین میکنه (با الویت بندی ای که برنامه نویس برای اون نخ مشخص میکنه . این الویت بندی ، در سربرگ Details از Task Manager ، با کلیک راست روی پروسه ی مورد نظر و انتخاب Set Priority و انتخاب گزینه ی مورد نظر ، علاوه بر اینکه در دسترس کاربران ویندوز هست تا بتونن الویت اجرای نخ اصلی پروسه ها را مشخص کنن ، وجود داره (دقت کن که کاربران ویندوز نمیتونن الویت بندی نخ های فرعی اون پروسه را مشخص کنن) ، در دسترس برنامه نویس ها هم هست تا بتونن الویت بندی نخ های فرعی خودشون را تعیین کنن که این الویت بندی ، باید قبل از اجرای نخ انجام بشه نه بعد از اجرای نخ) .
بقیه ی مبحث نخ ها را میذارم که عملکرد پردازنده ها را توضیح دادم . عملکرد پردازنده ها را هم در پست های بعدی توضیح میدم چون توضیح این پست زیاد شد .
Bookmarks