ممنون استاد علی
پس من این نتیجه رو گرفتم . :
اینکه هر پروسه (حداقل پروسه ی برنامه هایی که مما میسازیم) ، فقط توسط یک هسته و یک نخ در لحظه ، در حال اجرا هستن و این قضیه ی Task Manager ای که گفتم هر 4 هسته ، 40 درصد کل اش درگیره بخاطر اینه که مدام (در حد میلی ثانیه) پروسه مون در هر هسته (و نخ های اون هسته) ، در حال سوئیچ کردن هست و واسه همین ما توی Task Manager فکر میکنیم که همزمان 4 هسته درگیر هست درصورتی که این طور نیست
درسته؟
نه، الزاما یک نخ نیست، ممکنه هر چی نخ در اون هسته بوده در یک لحظه به اون پروسه اختصاص داده بشه. بستگی به این داره که برنامه تون چند نخی باشه.
در مورد Task Manager میشه همچین نظری داشت ولی کلا آمار دقیقی نمیده، اگه پردازنده 100 درصد در اختیار برنامه شما است اون Task Manager چطور داره روون کار می کنه؟
بعد اینکه چطور میشه یه برنامه ، حداقل از یک هسته میتونه تا 100 در 100 اش استفاده کنه؟ یعنی مثلا من توی برنامه ای که ساختم (فرضا همون مثال حلقه که 40 درصد از یه هسته رو درگیر میکرد) ، بخوام کاری کنم که تا حداکثر توان اون هسته از پردازنده استفاده کنه (تا سرعت پردازنده بالاتر بره) ، چی کار باید کنم؟ اصلا شدنی هست؟
اول ببینید برنامه تون داره چیکار می کنه، برنامه هایی که به حداکثر توان پردازنده احتیاج دارند نرم افزار های زیادی نیستند، انکودر فرمتی به فرمتی هستند، یا نرم افزار رمز گشایی، یا رندر گیری و ...
اگه یه برنامه عادی نوشتید که سرعت اجراش پایینه پس الگوریتم برنامه تون بد ئه و باید اصلاح بشه، حتی برای نرم افزار های فشرده ساز فایل هم اینطور موارد مرسوم نیست که بخواهند چند هسته ایش کنند، چون سرعت پایین دیسک ئه که عملا داره محدودشون می کنه نه پردازنده. وقتی دیسک تون نهایتا 100 مگابایت در ثانیه ذخیره کنه، سریعترین نرم افزار فشرده سازی هم روش اجرا بشه همچنان 100 مگابایت در ثانیه Save می کنه.
اشغال کردن 100 درصدی یک هسته ملاک سرعت برنامه شما نیست، چرا؟ چون شما می توانید انواع الگوریتم های مرتب سازی کند چند نخی بنویسید که همه شون نزدیک به 100 درصد از پردازنده استفاده کنند اما اندازه یک الگوریتم سریع تک نخی که 25 درصد توان پردازنده رو مشغول می کنه کارایی نداشته باشند. چرا همه برنامه ها از چند هسته نمی توانند استفاده بهینه بکنند؟ چون هر الگوریتمی رو نمیشه بصورت موازی تو چند بخش اجرا کرد. الگوریتم هایی که قابلیت موازی سازی دارند محدود اند. چند نخی بودن به تنهایی زمان اجرا رو بالا نمی بره.
تو Task Manager می توانید اولویت اجرای هر پروسه عادی ای رو با راست کلیک تغییر بدید، اگه پایین بیاریدش اجرای سریع و یکنواخت اش براتون مهم نیست، ولی اگه زیادی بالا ببریدش سیستم اونقدر مشغول اش میشه که عملا مجال کنترل سایر اجزاء ویندوز رو از دست میده و رابط کاربری سیستم میره تو کما. ویندوز نیست که هنگ کرده، برنامه تونه که معطلش می کنه، خود برنامه تون در کما نمیره، در حال اجرا است ولی مجالی به واسط کاربری سیستم داده نمیشه. این خودش خیلی بد ئه، کاربر ها از همچین برنامه ای خوششون نمیاد. حتی نرم افزار های خیلی سنگین محاسباتی هم همچین روالی ندارند که کل توان پردازنده رو صرف خودشون کنند.
2 تا سئوال دارم . کلا قشنگ متوجه ی اختصاص حافظه و حذف شدن متغییرها و اشیاء از حافظه در سی شارپ نشدم
اینایی که میگم ، درسته؟ :
وقتی یه متغییر تعریف میکنیم ، نام اون متغییر رو در حافظه ی استک رم (بصورت کد اسکی) اشغال میکنه (مثلا وقتی مینویسیم int a ، در حافظه ی استک رم ، عدد 97 برای این متغییر با این نام در نظر میگیره) و بسته به نوع داده (کلاس یا استراکچر) چه مقدار به متغییرمون بدیم یا نه ، براش حافظه ای در هیپ (برای کلاس) و یا در استک (برای استراکچر) در نظر میگیره . اگه مقداردهی (اولیه یا بعدا) بشه که مشخص هست ولی اگه مقداردهی نشه ، بسته به null پذیر بودن ، براش باز هم حافظه میگیره اما مقدار 0 (که همون گفته بودین مقار null همون 0 هست) برای همه ی خونه هاش در نظر میگیره (فقط اینجا سئوالی که پیش میاد اینه که نمیدونم چرا بعضی از متغییرها که null میشن ، ارور زمان اجرا (که شی ای بهش اختصاص داده نشد) میدن ولی بعضی ها ارور نمیدن و مقدار پیش فرضش رو ذخیره میکنن!! و نمیدونم دقیق کدوما ارور میدن یا کدوما نمیدن)
اینکه اسم متغیر با کد ASCII در حافظه Stack قرار می گیره رو نمیدونم از چه منبعی نقل می کنید ولی کلا تصوری که از نحوه کامپایل شدن کد سی شارپ و اجرا شدن کد زبان ماشین مجازی اش دارید فقط اشتباه نیست، به تفسیر قواعد زبان ربطی نداره. شما سعی می کنید از روی ساختار پشت پرده یک روالی که از دید برنامه نویس پنهانه برای قواعد کلی یک زبان سطح بالا استدلال پیدا کنید. انگار که از روی نحوه کار موتور خودرو قواعد راهنمایی و رانندگی رو تفسیر کنید. قبلا در مورد متفاوت متغیر محلی و متغیر سراسری صحبت کردیم. اینکه متغیر محلی حتما باید مقدار دهی بشه تا قابل دسترسی باشه و مقدار پیشفرض قبول نمی کنه قاعده کلی زبان ئه، ربطی به Stack و Heap و Hash Table و ... که نداره. قبلا عرض کردم خدمتتون که متغیر محلی باید مقدار دهی بشه، حالا اینکه تو حافظه چطور بود و امکانش بود یا نبود که قواعد زبان نیست.
شما کدوم متغیر سراسری ای تعریف کردید که مقدار بهش ندید و مقدار پیشفرض نداشته باشه؟ یا کدوم متغیر محلی ای تعریف کردید که بدون مقدار دادن مقدار پیشفرض داشته باشه؟ یک قاعده تصادفی و غیر قابل پیش بینی یا خیلی پیچیده که نیست. اگه ملاک قواعد، زبان ساختار داخلی حافظه بود که در هر متغیر رشته ای میشد یک متغیر float ئه 32 بیتی رو جا داد، قواعد زبان این اجازه رو نمیده، ربطی که به این متغیر کجا میشینه و اسمش ثبت میشه یا نمیشه نداره.
کلمه ی کلیدی new که هر بار برای متغییری از نوع استراکچرها بکار بره ، یه حافظه ی جدید رو براشون در نظر میگیره (مثل نوع کلاس ها) یا اینکه همون حافظه ی قبلی گرفته شده رو اوررایت میکنه؟
بعد از چند بار گفتگوی مفصل سر حافظه قبلی و Overwrite کردن و نکردن باز بر می گردید سر خونه اول، قبلا در مورد این مساله مفصل صحبت کردیم.
مشکل بیشتر من اینه که نمیدونم مدیریت clr و حتی کمپایلر برای آزادسازی حافظه ی رم در متغییرها و اشیاء دقیقا چطورن . هر چند مشکل پست بالا رو که توضیح دادین متوجه شدم ولی سئوالات در این باره زیاد دارم . مثلا اگه این طور در نظر بگیریم که clr ، وقتی به بلوک ای میرسه که بسته شد ، در اون صورت اشاره گر (یا مقدار برای استراکچر) رو منقضی و پاک میکنه (مثل وقتی که دو تابع تعریف میکنیم ، چون هر تابع بلاک ای داره ، وقتی کنترل clr به بلاک پایانی یه تابع میرسه ، حافظه ی متغییر پاک و آزاد میشه و واسه همین میتونیم در تابع دیگه چون در بلاک جدیدی هست ، همون متغییری که در تابع قبلی تعریف کردیم رو بدون هیچ مشکلی تعریف کنیم) :
کد:
private void button1_Click(object sender, EventArgs e)
{
int a = 5;
}
private void button2_Click(object sender, EventArgs e)
{
int a = 3;
}
مشکل تون که قطعا ابهام هست، ولی دلیل بروز ابهام قواعد زبان نیست، ابهام رو خودتون درست می کنید، چون بر اساس یکسری شواهد اولیه فورا قاعده اختراع می کنید و بعد برای قاعده تون دنبال استدلال می گردید که چون در اغلب موارد قاعده نادرستی بوده به مثال نقض بر می خورید. شما تعریف متغیر رو که مربوط به کامپایل ئه ربط می دید به تخصیص حافظه که مربوط به زمان اجرا است و آزاد شدن حافظه متغیر در زمان اجرا رو که اختیارش دست GC ئه رو هم ربط میدید به آزاد شدن اسم متغیر رزرو شده و محدودیت های زمان کامپایل و قواعد کلی زبان #C رو ربط میدید به CLR.
فرض کنید که قرار بود متد
button1_Click و
button2_Click رو دو تا نخ مجزا همزمان اجرا کنند، توقع دارید که پیغام خطا بده که متغیر a قبلا در حافظه ثبت شده و دیگه نمیشه a دیگه ای در حافظه باشه؟ وقتی نوبت به CLR رسید دیگه اصلا اسم متغیر مطرح نیست، CLR با زبان ماشین مجازی کار می کنه، نه زبان #C، برنامه هایی که اسم متغیر هاشون طولانی اند که قرار نیست کند تر اجرا بشوند.
سئوال بعدی اینه که قضیه ی jagged array ، بصورت واقعی آرایه درون آرایه هست (یعنی مثلا یه jagged array دو تایی مثل int[][] ، برای واکشی اعضاش ، نیاز به دو تا حلقه ی foreach هست) ولی ساختار و قضیه ی آرایه ی چند بعدی مثل int[,] چجوری هست که حتی با یه حلقه ی foreach هم میشه همه ی اعضاش رو واکشی کرد؟ یعنی کلش یه آرایه هست ولی چند قسمت میتونه بشه؟! (ممنون میشم فرق دو ساختار رو هم در رم بگین یعنی بگین در رم ساختار هر کدوم که ایجاد میشن ، چطوره؟)
ممنون
اینکه foreach برای چه کلاس هایی کار می کنه به پیاده سازی IEnumerable مربوطه نه ساختار حافظه اش، چه در ساختارشون ببینید و چه نبینید متد یا متد هایی دارند به نام GetEnumerator. اونه که foreach اجراش می کنه.
قبلا براتون در موردGetEnumerator گفته بودم. یک شی ای با ساختار IEnumerator بر می گردونه که foreach با اون اعضاء رو پیمایش می کنه، اونم با هر ترتیب و شیوه و تعدادی که IEnumerator ئه بخواد. کد نویسی میشه پس قانون مشخص و ثابتی نداره. وقتی تعریف یک ساختاری پیچیده باشه، دیگه NET. بصورت خودکار براش نمیتونه متد GetEnumerator ابداع کنه، خودتون باید GetEnumerator ای بنویسید که اعضاء رو به شیوه دلخواه خودتون پیمایش کنید. foreach فقط چیزی رو پیمایش می کنه که GetEnumerator به عنوان یک IEnumerator بهش تحویل می ده. اگه می بینید برای کلاسی foreach جواب میده به این دلیل ئه که قبلا براشون IEnumerator طراحی شده، برای بعضی ساختار های پایه ای زبان همه تعاریف رو کامل نشون نمیده ولی وجود دارند :
کد:
var a = new int[10];
MessageBox.Show((a is IEnumerable<int>).ToString());
این اصلا ربطی به این نداره که فلان ساختار واقعا آرایه است یا نیست یا jagged ئه یا نیست. اگه قبلا IEnumerator تعریف شده باشه کار میکنه و اگه تعریف نشده باشه کار نمی کنه.