گفتگو هایی در باب سی شارپ

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ممنون
من دقیق متوجه نشدم . الان یعنی الویت عملگر == از & و | و ^ بیشتره؟ یعنی اول == رو حساب و بعد & رو محاسبه میکنه؟ مگه الویت عملگر == با = در یک حد نیست؟ اگه آره ، پس این فرض اشتباه درمیاد
اگه نه ، من قشنگ متوجه نشد . پس در این صورت اگه بخایم درست عمل کنه ، باید این طور مینوشتیم دیگه (به پرانتزها دقت کنید) :
کد:
if ( (e.KeyData == (Keys.Control | Keys.ControlKey) ) & (GetAsyncKeyState(Keys.LControlKey) < 0) )
ولی ما که این طور ننوشتیم . پس یا باید فرضیه ی بالا درست باشه (که فکر نکنم این طور باشه) یا کلا من متوجه نشدم بازم که چرا کمپایلر ارور نمیده

این تیکه رو متوجه شدم
قضیه همون تقدم عملگرها بود که الان فهمیدم تقدم عملگر == از عملگر = و & و | بیشتره (قبلا فکر میکردم تقدم شون در حد هم هستن)
حالا اون 2 تا سئوال هست
ممنون
 

the_king

مدیرکل انجمن
متوجه شدم
اما بدست آوردن جواب این جوری سخت میشه که . باید خیلی از اعداد رو تست کنیم تا ببینیم کدوم عدد ، بجز کم ارزش ترین بیت شون ، کدوم بیت هاشون شبیه هم هستن . یعنی سخت میشه از عدد 1 فهمید عدد 4 و 5 با هم ایکس اور شدن
برای بدست آوردن معکوس (یعنی بدست آوردن اعدادی که ایکس اور شدن) ، مثل قضیه ی اور ، باید جواب را با دونه دونه ی اعداد ، اَند & کرد تا متوجه شد؟
اصلا روالی که میخواهید منطق ریاضی نداره که بخواهید همچین ایده ای بدید. XOR که نمیتونه دو تا مجهول رو مشخص کنه. دارید یک معما طرح می کنید که ممکنه روی یک مجموعه عدد جواب منحصر بفرد بده و روی یک مجموعه دیگه تعداد زیادی پاسخ داشته باشه. چند تا عدد با هم xor شدن حاصل شده یک مقداری. انگار که چند عدد با هم جمع شدند حاصل شده 100. ممکنه در مجموعه اعداد زیادی باشند که جمع شون بشه 100.
بجای این حدس و گمان میان دو تا مجموعه رو با هم مقایسه می کنند.
درباره ی خروجی تابع GetAsyncKeyState فقط در حد اختصار توضیح میدین؟
در MSDN ببینید، چرا از من می پرسید؟ اگه با ارزش ترین بیت (بیت علامت) ست شده باشه (یک باشه) کلید مورد نظر فشرده شده. برای همینه که چک میکنیم که مقدارش منفی ئه یا نه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
بله ولی چند تا نکته وجود داره، اولا یک هسته معمولا معادل یک نخ اجرایی نیست، فرضا یک هسته می تونه دو نخ همزمان رو اجرا کنه، یعنی یک پردازنده 4 هسته ای جمعا 8 نخی ئه. ثانیا پردازنده معمولا نمی تونه روی دو تا هسته مجزا نخ های یک پروسه رو اجرا کنه، باید روی یک هسته اجرا بشن، معنی اش اینه که اگه برنامه تون 10 تا نخ هم باشه، روی یک هسته و فقط دو تا نخ همزمان کار می کنه. اگه پردازنده 4 هسته ای باشه تنها از یک چهارم توان پردازنده کار می کشه.
برنامه هایی که برای استفاده از سیستم های چند هسته ای طراحی می شوند اصول خاصی رو رعایت می کنند، اولا مناسب هر برنامه ای نیست و ثانیا بیشتر از یک پروسه دارند، یعنی فرضا از یه فایل اجرایی بیش از یک نسخه رو اجرا می کنند، برای اینکار هماهنگی پروسه ها و نخ ها خودش مساله مهمیه.


اولا همانطور که گفتم در نظر بگیرید که هر هسته فرضا 2 نخ رو همزمان اجرا می کنه. اگه تعداد نخ های سیستم واگذار شده به یک نخ فیزیکی پردازنده رو در نظر بگیرید، یه کسر کوچیکی از ثانیه یکی شون رو اجرا می کنه، متوقف اش می کنه، میره سراغ نخ بعدی و یه کسری از ثانیه اونو اجرا می کنه و ... عملا همزمانی اجرا فقط برای نخ های فیزیکی ئه، اون چیزی که بین این هزاران نخ سیستم می بینید واقعا اجرای موازی نیست. سوئیچ کردن های فوق العاده سریع ئه.

اولا خیلی از سرویس ها مدام اجرا می شوند و متوقف می شوند، یعنی تعداد پروسه ها و در نتیجه نخ ها ثابت نیست. ثانیا اگر یک پروسه ای هم مدام در حال اجرا است، دلیل نمیشه که تعداد نخ های درخواستی اش ثابت بشه، فرضا زمانی که یک رویدااد خاص رخ داد درخواست نخ جدید می کنه و بعد چند لحظه که کار نخ تموم شد نخ نابود میشه. اینطوری نیست که همون اول اجرا 10 تا نخ بسازه و تا آخر اجرا همون 10 نخ در حال اجرا بمونه.

سلام
من یه چیزی رو خوب متوجه نشدم استاد علی
الان پردازنده ی من 4 هسته ای 4 نخ هست (i5 4460)
شما هم گفتین که هر پروسه ، فقط توی یه هسته اجرا میشه
هر هسته ی من هم فقط یه نخ رو میتونه اجرا کنه یعنی i3 یا i7 نیست که 2 نخ همزمان رو اجرا کنه
پس با این حال نباید وقتی توی ویژال استودیو یه نخ مجزا مینویسم ، تا نخ جاری رو تموم نکرد ، همزمان بتونه نخ جدید رو اجرا کنه . یا به عبارتی ، وقتی در این پردازنده ، من یه نخ رو متوقف (pause) میکنم ، و تا زمانی که نخ متوقف شده رو ادامه ندم (که تموم شه) ، نخ جدید رو نباید اجرا کنه ولی نخ جدید رو اجرا میکنه
پس به نظرتون این نشون دهنده ی این نیست که همزمان از چند هسته میتونه استفاده کنه (و از نخ های هسته های دیگه استفاده میکنه) ؟
سند دیگه از اجرای یه پروسه توسط چند هسته به نظرم اینه که مثلا یه کد با اجرای طولانی نوشته شه (مثلا یه حلقه ای که صرفعا چند صد میلیارد رو بشماره) ، بعد توی Task Manager میبینیم که هر هسته درگیر هست اما هر کدوم به میزان مثلا 30 درصد
پس نمیتونیم این نتیجه رو بگیریم که هر پروسه مون توسط چند هسته داره پردازش میشه؟
 

the_king

مدیرکل انجمن
سلام
من یه چیزی رو خوب متوجه نشدم استاد علی
الان پردازنده ی من 4 هسته ای 4 نخ هست (i5 4460)
شما هم گفتین که هر پروسه ، فقط توی یه هسته اجرا میشه
هر هسته ی من هم فقط یه نخ رو میتونه اجرا کنه یعنی i3 یا i7 نیست که 2 نخ همزمان رو اجرا کنه
پس با این حال نباید وقتی توی ویژال استودیو یه نخ مجزا مینویسم ، تا نخ جاری رو تموم نکرد ، همزمان بتونه نخ جدید رو اجرا کنه . یا به عبارتی ، وقتی در این پردازنده ، من یه نخ رو متوقف (pause) میکنم ، و تا زمانی که نخ متوقف شده رو ادامه ندم (که تموم شه) ، نخ جدید رو نباید اجرا کنه ولی نخ جدید رو اجرا میکنه
پس به نظرتون این نشون دهنده ی این نیست که همزمان از چند هسته میتونه استفاده کنه (و از نخ های هسته های دیگه استفاده میکنه) ؟
سند دیگه از اجرای یه پروسه توسط چند هسته به نظرم اینه که مثلا یه کد با اجرای طولانی نوشته شه (مثلا یه حلقه ای که صرفعا چند صد میلیارد رو بشماره) ، بعد توی Task Manager میبینیم که هر هسته درگیر هست اما هر کدوم به میزان مثلا 30 درصد
پس نمیتونیم این نتیجه رو بگیریم که هر پروسه مون توسط چند هسته داره پردازش میشه؟
نه، لیست Task Manager رو ببینید، اون همه سرویس و پروسه در حال اجرا هستند، حتی با پردازنده 32 هسته ای هم نمیشد به هر کدوم اختصاصا هسته فیزیکی تخصیص داد.
نخ ای که در اختیار برنامه شما قرار می گیره نخ فیزیکی که نیست، اگه فیزیکی بود تا ابد به برنامه شما نخ نمی رسید، چون خود ویندوز به تنهایی اونقدر نخ میخواد که نوبت به برنامه شما نمیرسه.
در خلال اجرای نخ برنامه شما بارها و کسری از ثانیه نخ فیزیکی بین اجرای نخ برنامه شما و اجرای سایر نخ ها سوئیچ کرده، این اجرای همزمان نیست. به نظرتون همزمان میاد. اینطوری ئه که میتونه تعداد زیادی نخ رو اجرا کنه.
و فرض کنیم برنامه شما 16 نخ در حال اجرا داره و میزان اشغال CPU رو به 100% رسوندید ولی همون زمان دارید با ماوس و Task Manager بدون مشکل کار می کنید. این سوئیچ کردن مداوم بین نخ ها همزمانی نیست، حتی قبلتر از اینکه پردازنده های چند هسته ای تولید بشوند هم وجود داشته. برنامه شما هسته های دیگه رو هم درگیر می کنه ولی نه اینکه همزمان چهار تا هسته رو برنامه شما قبضه کنه. سیستم که این همه سرویس و پروسه حیاتی خودش رو معلق نگه نمیداره.
پس به نظرتون این نشون دهنده ی این نیست که همزمان از چند هسته میتونه استفاده کنه (و از نخ های هسته های دیگه استفاده میکنه) ؟
دو تا مفهوم concurrency و parallelism داریم که اولی اون چیزی که داره اتفاق می افته و دومی چیزی که حس می کنید رخ میده. تو سیستمی که کلا چهار تا نخ بیشتر نداره اگه پردازنده بخواد 4 تا نخ به برنامه شما نخصیص بده دیگه کدوم نخ سیستم رو اداره کنه؟ کدوم نخ منابع رو تخصیص بده؟ بقیه برنامه ها با مدیریت کدوم نخ مجال اجرا پیدا کنند؟

سند دیگه از اجرای یه پروسه توسط چند هسته به نظرم اینه که مثلا یه کد با اجرای طولانی نوشته شه (مثلا یه حلقه ای که صرفعا چند صد میلیارد رو بشماره) ، بعد توی Task Manager میبینیم که هر هسته درگیر هست اما هر کدوم به میزان مثلا 30 درصد
پس نمیتونیم این نتیجه رو بگیریم که هر پروسه مون توسط چند هسته داره پردازش میشه؟
بله، ولی این یک نخ نیست که توسط چند هسته اجرا میشه، نخ های پروسه تون هر کدوم در مقطعی کسری از ثانیه توسط یک نخ فیزیکی ای از بین یکی از هسته ها اجرا میشه. اینها موازی هم اجرا نمیشن.
سیستم یک لیست بزرگ از پروسه ها داره و یک تعداد محدودی نخ فیزیکی که از نظر فیزیکی یکسری محدودیت هایی هم دارند. فرضا سوئیج کردن اجرای یک پروسه بین دو نخ در یک هسته هزینه کمتری داره تا سوئیچ بین دو نخ در دو هسته متفاوت. سیستم در هر کسری از ثانیه به هر کدوم از نخ های فیزیکی یک نخ از پروسه ای رو تخصیص میده بعد مجددا معلق اش می کنه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
نه، لیست Task Manager رو ببینید، اون همه سرویس و پروسه در حال اجرا هستند، حتی با پردازنده 32 هسته ای هم نمیشد به هر کدوم اختصاصا هسته فیزیکی تخصیص داد.
نخ ای که در اختیار برنامه شما قرار می گیره نخ فیزیکی که نیست، اگه فیزیکی بود تا ابد به برنامه شما نخ نمی رسید، چون خود ویندوز به تنهایی اونقدر نخ میخواد که نوبت به برنامه شما نمیرسه.
در خلال اجرای نخ برنامه شما بارها و کسری از ثانیه نخ فیزیکی بین اجرای نخ برنامه شما و اجرای سایر نخ ها سوئیچ کرده، این اجرای همزمان نیست. به نظرتون همزمان میاد. اینطوری ئه که میتونه تعداد زیادی نخ رو اجرا کنه.
و فرض کنیم برنامه شما 16 نخ در حال اجرا داره و میزان اشغال CPU رو به 100% رسوندید ولی همون زمان دارید با ماوس و Task Manager بدون مشکل کار می کنید. این سوئیچ کردن مداوم بین نخ ها همزمانی نیست، حتی قبلتر از اینکه پردازنده های چند هسته ای تولید بشوند هم وجود داشته. برنامه شما هسته های دیگه رو هم درگیر می کنه ولی نه اینکه همزمان چهار تا هسته رو برنامه شما قبضه کنه. سیستم که این همه سرویس و پروسه حیاتی خودش رو معلق نگه نمیداره.

دو تا مفهوم concurrency و parallelism داریم که اولی اون چیزی که داره اتفاق می افته و دومی چیزی که حس می کنید رخ میده. تو سیستمی که کلا چهار تا نخ بیشتر نداره اگه پردازنده بخواد 4 تا نخ به برنامه شما نخصیص بده دیگه کدوم نخ سیستم رو اداره کنه؟ کدوم نخ منابع رو تخصیص بده؟ بقیه برنامه ها با مدیریت کدوم نخ مجال اجرا پیدا کنند؟

بله، ولی این یک نخ نیست که توسط چند هسته اجرا میشه، نخ های پروسه تون هر کدوم در مقطعی کسری از ثانیه توسط یک نخ فیزیکی ای از بین یکی از هسته ها اجرا میشه. اینها موازی هم اجرا نمیشن.
سیستم یک لیست بزرگ از پروسه ها داره و یک تعداد محدودی نخ فیزیکی که از نظر فیزیکی یکسری محدودیت هایی هم دارند. فرضا سوئیج کردن اجرای یک پروسه بین دو نخ در یک هسته هزینه کمتری داره تا سوئیچ بین دو نخ در دو هسته متفاوت. سیستم در هر کسری از ثانیه به هر کدوم از نخ های فیزیکی یک نخ از پروسه ای رو تخصیص میده بعد مجددا معلق اش می کنه.

ممنون
من هم یه جوری فهمیدم اما بازم قشنگ درک نکردم
الان شما میگین که بصورت تصادفی ، سیستم یه نخ از یه هسته رو به برنامه ی مورد نظر میده و بعد از اتمام ، پس میگیره؟
پس توی پردازنده ی 4 هسته ای 4 نخ من ، فرض کنید یه برنامه ای مینویسیم که 4 تا نخ داره و توی هر 4 تا کد توقف اجرای نخ رو مینویسیم . توی نخ اول ، کدی نوشتیم که نخ متوقف بشه . خوب مشخص هست که کد وقتی تموم نشه (متوقف شده باشه) ، نخ فیزیکی پردازنده پس گرفته نمیشه . درسته؟
پس یه نخ فعلا مشغول هست
بعد توی نخ دوم هم کد توقف رو مینویسیم . اینم باید همین اتفاق بیافته تا کد نخ چهارم
پس با این اوصاف هر 4 نخ فیزیکی باید کامل اشغال بشن!!

-----------------------------

سئوال بعدی اینکه در کد زیر ، اولین متغییر a فقط در اون بلاک تعریف شده هست . بیرون از بلاک که بره ، اشاره گرش رو از دست میده . قبلا گفتین که متغییرهای null رو میشه با دستور GC.Collect() کاری کرد که clr مجبور بشه بصورت فوری از رم همون لحظه آزاد بشن
اگه آزاد بشن ، پس باید بعد از اون بشه دوباره متغییر a (همون نام) رو تعریف کرد پس در کد زیر چرا نمیشه؟ :

کد:
private void button1_Click(object sender, EventArgs e)
        {
            {
                int? a = 10;
                a = null;
            }
            GC.Collect();
            int a = 2;
        }
 

the_king

مدیرکل انجمن
ممنون
من هم یه جوری فهمیدم اما بازم قشنگ درک نکردم
الان شما میگین که بصورت تصادفی ، سیستم یه نخ از یه هسته رو به برنامه ی مورد نظر میده و بعد از اتمام ، پس میگیره؟
پس توی پردازنده ی 4 هسته ای 4 نخ من ، فرض کنید یه برنامه ای مینویسیم که 4 تا نخ داره و توی هر 4 تا کد توقف اجرای نخ رو مینویسیم . توی نخ اول ، کدی نوشتیم که نخ متوقف بشه . خوب مشخص هست که کد وقتی تموم نشه (متوقف شده باشه) ، نخ فیزیکی پردازنده پس گرفته نمیشه . درسته؟
اولا تصادفی نیست، اصلا توی بحث مدیریت منابع روش تصادفی فقط زمانی کاربرد داره که تمامی درخواست کننده ها در شرایط یکسانی باشند یا اونقدر شناسایی اولویت ها دشوار و سنگین باشه که به صرف زمان محاسبه اش نیارزه.
ثانیا پس گرفته میشهو اتفاقا بی رحمانه و به زور پس گرفته میشه. نرم افزار شما مالک منابع اشتراکی سیستم نیست و سیستم عامل همه منابع اشتراکی رو اعم از حافظه و پردازنده و دیسک و ... رو به مدت کوتاهی اجاره میده و هر زمان هم که تشخیص بده که اولویت برنامه تون از مورد دیگه ای کمتر ئه یا برنامه تون بیش از زمان مشخصی منبع رو در اختیار داشته پسش می گیره و برنامه تون رو معلق می کنه. اینکار مدام در حال انجامه. اگه فقط چند ثانیه این روال بی رحمانه و مالکانه رو نداشت مطمئن باشید که سیستم قفل می کرد چون برنامه ها که ملاحظه همدیگه رو نمی کنند، فقط دلشون میخواد منابع مورد نیازشون یکجا و تمام و کمال در اختیارشون باشه. کاری ندارند که تا چند ثانیه چقدر از این منابع درخواستی شون واقعا لازمه. نکته مهم اینجا است که برنامه ها بیشتر از اینکه از پردازنده استفاده مفید کنند میخواهند بی مورد اشغالش نگهش دارند، سیستم عامل پسش می گیره تا حداکثر استفاده رو ازش ببره. کلا تلاش مدیریت منابع همینه که از منابع با حداکثر کارایی ممکن استفاده بشه. اینکه برنامه شما دلش نخواد که اجرای نخ اش متوقف بشه طبیعیه ولی سیستم عامل هم توقع نداره که برنامه شما مملو از کد توقف داوطلبانه باشه.


پس یه نخ فعلا مشغول هست
بعد توی نخ دوم هم کد توقف رو مینویسیم . اینم باید همین اتفاق بیافته تا کد نخ چهارم
پس با این اوصاف هر 4 نخ فیزیکی باید کامل اشغال بشن!!


سئوال بعدی اینکه در کد زیر ، اولین متغییر a فقط در اون بلاک تعریف شده هست . بیرون از بلاک که بره ، اشاره گرش رو از دست میده . قبلا گفتین که متغییرهای null رو میشه با دستور GC.Collect() کاری کرد که clr مجبور بشه بصورت فوری از رم همون لحظه آزاد بشن
اگه آزاد بشن ، پس باید بعد از اون بشه دوباره متغییر a (همون نام) رو تعریف کرد پس در کد زیر چرا نمیشه؟ :

کد:
private void button1_Click(object sender, EventArgs e)
        {
            {
                int? a = 10;
                a = null;
            }
            GC.Collect();
            int a = 2;
        }
GC.Collect ربطی به تعریف متغیر نداره، کامپایلر نمی تونه روال اجرای کد رو پیشبینی بکنه و بگه وقتی اجرا به کد int a = 2 رسید اون GC.Collect اجرا شده یا نشده، فقط این رو در نظر می گیره که قبلا متغیری به نام a تعریف شده، پس کد قابل قبول نیست، همین. در ضمن آزاد شدن حافظه متغیر از حافظه که کاری به اسامی متغیر ها که نداره. شما حافظه متغیر a رو هم آزاد کنید اسمش که آزاد نشده. اسم متغیر موقعی که کد کامپایل میشه مشخصه، اینکه کی حافظه بهش داده میشه و کی ازش گرفته میشه یه بحث جدا است.از طرف دیگه GC.Collect رو به عنوان یک درخواست از پایین در نظر بگیرید نه فرمان از بالا. GC الزاما همون لحظه درخواست، کاری رو بصورت کامل انجام نمیده، مدیریتش مستقل ئه و ناهماهنگ با برنامه ها.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
اولا تصادفی نیست، اصلا توی بحث مدیریت منابع روش تصادفی فقط زمانی کاربرد داره که تمامی درخواست کننده ها در شرایط یکسانی باشند یا اونقدر شناسایی اولویت ها دشوار و سنگین باشه که به صرف زمان محاسبه اش نیارزه.
ثانیا پس گرفته میشهو اتفاقا بی رحمانه و به زور پس گرفته میشه. نرم افزار شما مالک منابع اشتراکی سیستم نیست و سیستم عامل همه منابع اشتراکی رو اعم از حافظه و پردازنده و دیسک و ... رو به مدت کوتاهی اجاره میده و هر زمان هم که تشخیص بده که اولویت برنامه تون از مورد دیگه ای کمتر ئه یا برنامه تون بیش از زمان مشخصی منبع رو در اختیار داشته پسش می گیره و برنامه تون رو معلق می کنه. اینکار مدام در حال انجامه. اگه فقط چند ثانیه این روال بی رحمانه و مالکانه رو نداشت مطمئن باشید که سیستم قفل می کرد چون برنامه ها که ملاحظه همدیگه رو نمی کنند، فقط دلشون میخواد منابع مورد نیازشون یکجا و تمام و کمال در اختیارشون باشه. کاری ندارند که تا چند ثانیه چقدر از این منابع درخواستی شون واقعا لازمه. نکته مهم اینجا است که برنامه ها بیشتر از اینکه از پردازنده استفاده مفید کنند میخواهند بی مورد اشغالش نگهش دارند، سیستم عامل پسش می گیره تا حداکثر استفاده رو ازش ببره. کلا تلاش مدیریت منابع همینه که از منابع با حداکثر کارایی ممکن استفاده بشه. اینکه برنامه شما دلش نخواد که اجرای نخ اش متوقف بشه طبیعیه ولی سیستم عامل هم توقع نداره که برنامه شما مملو از کد توقف داوطلبانه باشه.

ممنون استاد علی
پس من این نتیجه رو گرفتم . :
اینکه هر پروسه (حداقل پروسه ی برنامه هایی که مما میسازیم) ، فقط توسط یک هسته و یک نخ در لحظه ، در حال اجرا هستن و این قضیه ی Task Manager ای که گفتم هر 4 هسته ، 40 درصد کل اش درگیره بخاطر اینه که مدام (در حد میلی ثانیه) پروسه مون در هر هسته (و نخ های اون هسته) ، در حال سوئیچ کردن هست و واسه همین ما توی Task Manager فکر میکنیم که همزمان 4 هسته درگیر هست درصورتی که این طور نیست
درسته؟
بعد اینکه چطور میشه یه برنامه ، حداقل از یک هسته میتونه تا 100 در 100 اش استفاده کنه؟ یعنی مثلا من توی برنامه ای که ساختم (فرضا همون مثال حلقه که 40 درصد از یه هسته رو درگیر میکرد) ، بخوام کاری کنم که تا حداکثر توان اون هسته از پردازنده استفاده کنه (تا سرعت پردازنده بالاتر بره) ، چی کار باید کنم؟ اصلا شدنی هست؟


GC.Collect ربطی به تعریف متغیر نداره، کامپایلر نمی تونه روال اجرای کد رو پیشبینی بکنه و بگه وقتی اجرا به کد int a = 2 رسید اون GC.Collect اجرا شده یا نشده، فقط این رو در نظر می گیره که قبلا متغیری به نام a تعریف شده، پس کد قابل قبول نیست، همین.

آها
مثل کد زیر :

کد:
private void btnForeachVar1_Click(object sender, EventArgs e)
        {
            TextBox myTextBox = new TextBox();
            myTextBox.Text = "تکست باکس";
            Label myLabel = new Label();
            myLabel.Text = "لیبل";

            Control[] myObject = { this.btnForeachVar1, myTextBox, this, myLabel };
            foreach (var item in myObject)
                MessageBox.Show("controls type : "+item.GetType().Name+"\n\ncontrols text : "+item.Text);
        }
که نوع item در زمان اجرا ، هر بار برابر با نوع هر عضو آرایه ی myObject هست . یعنی یه بار Button و یه بار TextBox و ... هست ولی توی کمپایلر که نمیتونه هر بار تغییر کنه ، نوعش ، نوع پدر همه ی این انواع داده ای یعنی نوع Control هست
درسته؟
در ضمن آزاد شدن حافظه متغیر از حافظه که کاری به اسامی متغیر ها که نداره. شما حافظه متغیر a رو هم آزاد کنید اسمش که آزاد نشده. اسم متغیر موقعی که کد کامپایل میشه مشخصه، اینکه کی حافظه بهش داده میشه و کی ازش گرفته میشه یه بحث جدا است.از طرف دیگه GC.Collect رو به عنوان یک درخواست از پایین در نظر بگیرید نه فرمان از بالا. GC الزاما همون لحظه درخواست، کاری رو بصورت کامل انجام نمیده، مدیریتش مستقل ئه و ناهماهنگ با برنامه ها.

2 تا سئوال دارم . کلا قشنگ متوجه ی اختصاص حافظه و حذف شدن متغییرها و اشیاء از حافظه در سی شارپ نشدم
اینایی که میگم ، درسته؟ :
وقتی یه متغییر تعریف میکنیم ، نام اون متغییر رو در حافظه ی استک رم (بصورت کد اسکی) اشغال میکنه (مثلا وقتی مینویسیم int a ، در حافظه ی استک رم ، عدد 97 برای این متغییر با این نام در نظر میگیره) و بسته به نوع داده (کلاس یا استراکچر) چه مقدار به متغییرمون بدیم یا نه ، براش حافظه ای در هیپ (برای کلاس) و یا در استک (برای استراکچر) در نظر میگیره . اگه مقداردهی (اولیه یا بعدا) بشه که مشخص هست ولی اگه مقداردهی نشه ، بسته به null پذیر بودن ، براش باز هم حافظه میگیره اما مقدار 0 (که همون گفته بودین مقار null همون 0 هست) برای همه ی خونه هاش در نظر میگیره (فقط اینجا سئوالی که پیش میاد اینه که نمیدونم چرا بعضی از متغییرها که null میشن ، ارور زمان اجرا (که شی ای بهش اختصاص داده نشد) میدن ولی بعضی ها ارور نمیدن و مقدار پیش فرضش رو ذخیره میکنن!! و نمیدونم دقیق کدوما ارور میدن یا کدوما نمیدن)
کلمه ی کلیدی new که هر بار برای متغییری از نوع استراکچرها بکار بره ، یه حافظه ی جدید رو براشون در نظر میگیره (مثل نوع کلاس ها) یا اینکه همون حافظه ی قبلی گرفته شده رو اوررایت میکنه؟
مشکل بیشتر من اینه که نمیدونم مدیریت 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;
        }

اگه این تحلیل درسته ، پس چرا وقتی داخل خود تابع ، یه بلاک میسازیم (هر بلاکی مثل شرط ها و ...) ، پس چرا بیرون از اون بلاک دوباره نمیتونیم همون متغییر رو تعریف کنیم؟ :

کد:
private void button1_Click(object sender, EventArgs e)
        {
            {
                int a = 1;
            }
            int a = 5;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            int a = 3;
        }

تحلیل و تناقض این دو نوع کد توسط clr و کمپایلر چطوره؟
----------------
سئوال بعدی اینه که قضیه ی jagged array ، بصورت واقعی آرایه درون آرایه هست (یعنی مثلا یه jagged array دو تایی مثل int[][] ، برای واکشی اعضاش ، نیاز به دو تا حلقه ی foreach هست) ولی ساختار و قضیه ی آرایه ی چند بعدی مثل int[,] چجوری هست که حتی با یه حلقه ی foreach هم میشه همه ی اعضاش رو واکشی کرد؟ یعنی کلش یه آرایه هست ولی چند قسمت میتونه بشه؟! (ممنون میشم فرق دو ساختار رو هم در رم بگین یعنی بگین در رم ساختار هر کدوم که ایجاد میشن ، چطوره؟)
ممنون
 
آخرین ویرایش:

the_king

مدیرکل انجمن
ممنون استاد علی
پس من این نتیجه رو گرفتم . :
اینکه هر پروسه (حداقل پروسه ی برنامه هایی که مما میسازیم) ، فقط توسط یک هسته و یک نخ در لحظه ، در حال اجرا هستن و این قضیه ی 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 تعریف شده باشه کار میکنه و اگه تعریف نشده باشه کار نمی کنه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
البته این مثال شما استفاده کاملی از قابلیت using نیست چون درست بعد از using با ; بلوک رو بسته، یعنی { و } رو نداره، ولی در کل این using باعث شده که myRead در سطر بعدی کد دیگه وجود نداشته باشه و Dispose بشه.

کد همون تاپیک تغییر رنگ پس زمینه فرم در سی شارپ رو ببینید، برای ایجاد کردن شی از فرم ColorDialog از using استفاده کرده ام.
این using ارتباطی با اون using فضای نام نداره. این using یک فرمان ئه، برای اشیاء ای بکار میره که منابعی بهشون تخصیص داده میشه و باید هر چه زودتر که کار تون باهاش تمام شد آزاد بشوند، این جور اشیا کلاس شون Disposable اند، یعنی متدی به نام Dispose دارند.
StreamWriter و Bitmap و Font و Form و ... Disposable هستند و هر وقت کارتان باهاشون تمام شد در سریعترین زمان ممکن باید نابودشون کنید تا منابع محدود سیستم رو بی خودی اشغال نکنند.

ممکنه کد نویس فراموش کنه که وقتی کارش با این اشیاء تموم شد متد Dispose رو فراخوانی کنه. using راهنمای کامپایلر ئه که بفهمه شما تا کجای کد به اون شیء احتیاج دارید.
using به شما اجازه میده که داخل بلوک خودش از شیء استفاده کنید، وقتی از بلوک اش خارج شدید خودکار Dispose رو فراخوانی می کنه و منابع شیء بزودی آزاد میشه. شما ممکنه توی اون بلوک { و } اش صد ها سطر کد دیگه بنویسید ولی به هر حال وقتی
از اون بلوک خارج شدید شیء مورد نظر نابود شده محسوب میشه.
کد:
            using (var f = new Form())
            {
                //Your Code
                //Your Code
                //Your Code
            }
using فقط برای اشیاء Dispose شدنی قابل استفاده است و مثلا شما نمی توانید یک متغیر int رو using کنید :
کد:
[COLOR="#FF0000"]            using (var x = new int())
            {
                //Your Code
                //Your Code
                //Your Code
            }
[/COLOR]
یا نمی توانید شیء ای که داخل بلوک using تعریف شده بوده در بیرون بلوک استفاده کنید چون بیرون بلوک وجود نداره :
کد:
            using (var f = new Font("Tahoma", 14f))
            {
                //Your Code
                //Your Code
                //Your Code
            }
[COLOR="#FF0000"]            button1.Font = f;[/COLOR]
و البته در نظر بگیرید که مدیریت منابع و حافظه در #C با Garbage Collector زبان ئه و اینطوری نیست که چون Dispose رو فراخوانی کردید همان لحظه شیء رو نابود کنه. ممکنه اینکار با تاخیر انجام بشه.

استاد علی ، میگم این قضیه ی using ، مثل همون قضیه ای هست که گفتم اول شی مورد نظر رو null و بعد گفتین اگه GC.Collect() رو فراخونی کنیم ، اون شی رو بزودی پاک میکنه؟
بعد اینکه داخل بلاک using هم هر شی ای تعریف کنیم ، بعد از بلاک ، مثل داخل پرانتز using ، متد dispose رو فراخونی و از رم پاک میکنه؟ یا نه؟
کلاس هایی که بیشترین استفاده رو از منابع میکنن و از اینترفیس IDisposable ارث میبرن ، همینایی بود که گفتین؟ همه ی کلاس ها که از IDisposable ارث نمیبرن؟ اگه ارث نبردن ، میتونیم خودمون پیاده سازیش کنیم و از اون کلاس شی درست کنیم به منظور using کردن بصورتی که بعد از بلاک using ، از رم آزاد شه؟
--------------
در مقاله ی زیر :
زنگ سی شارپ – قسمت بیست و هفتم | webtarget
پاراگراف سوم ، پنج خط مونده به آخر ، اولا گفته :
" این عبارت تنها متغیر ob را در stack قرار می‌دهد (و به آن مقدار null می‌دهد) و هنگامی‌ که کلمه‌ی کلیدی new اجرا می‌شود، شیء این کلاس در heap ذخیره خواهد شد"
قسمت ی که گفته "هنگامی‌ که کلمه‌ی کلیدی new اجرا می‌شود، شیء این کلاس در heap ذخیره خواهد شد" رو اشتباه کرد؟ آخه گفته بودین که گرفتن حافظه ربطی به کلمه ی کلیدی new نداره . گفته بودین که حتی اگه هیچ مقداری هم ندیم ، حافظه میگیره اما برای کلاس ها null که همون مقدار صفر هست روذخیره میکنه توش
اما اینجا یه سئوال پیش میاد که اگه (در متغییر محلی) ، در شی ای از یه کلاس که میسازیم ، مقداری ندیم و null که همون صفر هست رو ذخیره کنه ، پس چطور گفته بودین که اگه مقدار ندیم ، یه حافظه ی از قبل پر شده و اطلاعات ناخواسته توش هست رو میگیره . در صورتی که اگه null باشه که گفته بودین همه ی خونه ها با 0 ذخیره میشن (پس دیگه اطلاعات ناخواسته باید پاک شن)

در ادامه ی مقاله نوشته :
"در نهایت هنگامی‌که برنامه به انتهای متد می‌رسد، متغیرهایی که در stack بودند همه‌گی پاک می‌شوند"
این فقط برای متد صادقه یا وقتی که به آخر هر بلاک ای (مثلا بلاک if و ...) برسه؟
دوم از اینکه وقتی از using استفاده میکنیم (یا شی ای رو null میکنیم و بعد GC.Collect() رو اجرا میکنیم) ، حافظه ی مربوط به heap شو پاک میکنه دیگه؟ درسته؟
 

the_king

مدیرکل انجمن
استاد علی ، میگم این قضیه ی using ، مثل همون قضیه ای هست که گفتم اول شی مورد نظر رو null و بعد گفتین اگه GC.Collect() رو فراخونی کنیم ، اون شی رو بزودی پاک میکنه؟

نه، using برای اینه که محدوده عمر فلان شیء مشخص بشه و حتما هم در هر شرایطی خارج از اون محدوده با Dispose از بین بره. شما با خیال راحت داخل اون بدنه using از return استفاده می کنید بدون اینکه نگران باشید که قبلش Dispose ای بکار نبردید. null کردن یک متغیر با متد Dispose یک شیء رو اجرا کردن فرق می کنه. شما یک متغیر رو null می کنید، به شیء ای که داخلش بوده که کاری ندارید. ممکنه اتفاقاتی در اون Dispose بیافته که اصلا ربطی به حافظه خود شیء نداشته باشه، بین آزاد کردن حافظه یک شیء و آزاد کردن یکسری منابع سیستم خیلی تفاوت ها هست. Dispose برای آزاد کردن حافظه خود شیء نیست، مدیریت اون حافظه به GC مربوطه و به برنامه نویس ربطی نداره، Dispose برای اموری ئه که فراتر از حافظه خود شیء ئه.


بعد اینکه داخل بلاک using هم هر شی ای تعریف کنیم ، بعد از بلاک ، مثل داخل پرانتز using ، متد dispose رو فراخونی و از رم پاک میکنه؟ یا نه؟

Dispose رو اجرا می کنه ولی کاری به حافظه RAM نداره، using که GC نیست، Dispose هم فقط یک متد ئه که میتونه داخلش کدی باشه یا نباشه.


کلاس هایی که بیشترین استفاده رو از منابع میکنن و از اینترفیس IDisposable ارث میبرن ، همینایی بود که گفتین؟ همه ی کلاس ها که از IDisposable ارث نمیبرن؟ اگه ارث نبردن ، میتونیم خودمون پیاده سازیش کنیم و از اون کلاس شی درست کنیم به منظور using کردن بصورتی که بعد از بلاک using ، از رم آزاد شه؟

کلاس هایی که منابعی رو از سیستم دریافت می کنند که بعدا باید آزاد بشه یا ساختار هایی رو تغییر می دهند که مستقل از خود شیء ئه باید موقع نابود شدن هر کاری که کردند رو برگردونند به وضعیت قبلی. Dispose و IDispose برای اونجور کلاس ها است. به بیشتر و کمتر بودن نیست. می تونید پیاده سازی اش کنید ولی هیچ ربطی به حافظه RAM نداره.

--------------
در مقاله ی زیر :
زنگ سی شارپ – قسمت بیست و هفتم | webtarget
پاراگراف سوم ، پنج خط مونده به آخر ، اولا گفته :
" این عبارت تنها متغیر ob را در stack قرار می‌دهد (و به آن مقدار null می‌دهد) و هنگامی‌ که کلمه‌ی کلیدی new اجرا می‌شود، شیء این کلاس در heap ذخیره خواهد شد"
قسمت ی که گفته "هنگامی‌ که کلمه‌ی کلیدی new اجرا می‌شود، شیء این کلاس در heap ذخیره خواهد شد" رو اشتباه کرد؟

نه اشتباه نکرده، دقت کنید که حافظه متغیر یک چیزه و حافظه شیء یک چیز دیگه، حافظه متغیر op یک اشاره گر ئه، با new که اشاره گر ساخته نمیشه، حافظه شیء ساخته میشه. شما 10 بار new رو اجرا کنید 10 تا شیء جدید ایجاد میشه با 10 تا حافظه جدید، اما اشاره گر op یک حافظه داره که از اول بوده و همون هم میمونه. ممکنه GC جاشو در طول اجرای برنامه در حافظه تغییر بده و آدرس حافظه اش تغییر کنه ولی به هر حال یکی ئه و تکثیر نمیشه.


آخه گفته بودین که گرفتن حافظه ربطی به کلمه ی کلیدی new نداره . گفته بودین که حتی اگه هیچ مقداری هم ندیم ، حافظه میگیره اما برای کلاس ها null که همون مقدار صفر هست روذخیره میکنه توش

نمیدونم از کجا نقل قول می کنید ولی برید دوباره همون متنی که من نوشته بودم رو بخونید، قطعا دارید حافظه متغیر رو با حافظه شیء اشتباه می گیرید، با new شیء جدید ساخته میشه که طبعا حافظه جدیدی داره و ربطی هم به حافظه متغیر نداره. قبلا هم تاکید کردم که موقع تعریف متغیر بهش حافظه تخصیص داده میشه، این حافظه متغیر ئه، ربطی به شیء که نداره. وقتی شیء ای نساخته باشید دیگه چه حافظه ای برای شیء باید باشه؟



اما اینجا یه سئوال پیش میاد که اگه (در متغییر محلی) ، در شی ای از یه کلاس که میسازیم ، مقداری ندیم و null که همون صفر هست رو ذخیره کنه ، پس چطور گفته بودین که اگه مقدار ندیم ، یه حافظه ی از قبل پر شده و اطلاعات ناخواسته توش هست رو میگیره . در صورتی که اگه null باشه که گفته بودین همه ی خونه ها با 0 ذخیره میشن (پس دیگه اطلاعات ناخواسته باید پاک شن)

اطلاعات الزاما ناخواسته نیست، مطمئن نیستم که داخلش null نوشته میشه، شاید بشه ولی به هر حال چون صحبت از متغیر محلی ئه و مقدارش بدون تزریق کد های ماشین مجازی قابل دسترسی نیست فرقی نمی کنه که داخلش null ئه یا یک مقدار ناخواسته از قبل مونده. فرقی نمی کنه که مقدار داخلش چیه، مهم اینه که کامپایلر اجازه نمیده قبل از مقدار دهی ازش مقدار بخونید. خونه ها؟ یک حافظه اشاره گر ئه، مثلا 32 بیت یا 64 بیت ئه، حافظه بزرگی نیست ها.

در ادامه ی مقاله نوشته :
"در نهایت هنگامی‌که برنامه به انتهای متد می‌رسد، متغیرهایی که در stack بودند همه‌گی پاک می‌شوند"
این فقط برای متد صادقه یا وقتی که به آخر هر بلاک ای (مثلا بلاک if و ...) برسه؟

احتمالش هست که پایان بلاک این اتفاق بیافته، اطلاعی ندارم، از اونجایی که مربوط به ساختار داخلی ماشین مجازی ئه برنامه نویس درگیرش نیست. ممکنه در نسخه جدید NET. این روال ها تغییر کنه.

دوم از اینکه وقتی از using استفاده میکنیم (یا شی ای رو null میکنیم و بعد GC.Collect() رو اجرا میکنیم) ، حافظه ی مربوط به heap شو پاک میکنه دیگه؟ درسته؟
using ربطی به GC.Collect نداره. null کردن متغیر هم ربطی به شیء ای که داخلش بوده نداره که بخواد Dispose اش رو اجرا کنه. null کردن یک متغیر به حافظه heap مربوط نیست، حافظه اشاره گر یک متغیر ئه که null میشه. تا وقتی که GC نخواد هیچ حافظه ای آزاد نمیشه که در GC.Collect ممکنه این کار رو انجام بده یا با تاخیر انجام بده.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ممنون استاد علی
میگم در کد زیر ، مگه وقتی متغییر b یوزینگ میشه ، شی ای که بهش اشاره میکنه یعنی متغییر a رو هم نباید پاک کنه؟ یعنی مقادیر b پاک باید بشه از رم دیگه؟ پس باید شی a رو از هیپ پاک کنه . پس بعد از بلاک using نباید شی a مون در دسترس باشه ولی چرا هست؟ :


کد:
private void btnUsing3_Click(object sender, EventArgs e)
        {
            Button a = new Button();
            a.Name = "btnDestructUsing";
            a.Text = "DestructUsing";
            a.BackColor = Color.WhiteSmoke;
            a.Location = new Point(1, 250);
            a.Size = new Size(150, 35);
            this.Controls.Add(a);
            using (Button b = a)
            {
               
            }
            MessageBox.Show(a.Text);
        }
 

the_king

مدیرکل انجمن
ممنون استاد علی
میگم در کد زیر ، مگه وقتی متغییر b یوزینگ میشه ، شی ای که بهش اشاره میکنه یعنی متغییر a رو هم نباید پاک کنه؟ یعنی مقادیر b پاک باید بشه از رم دیگه؟ پس باید شی a رو از هیپ پاک کنه . پس بعد از بلاک using نباید شی a مون در دسترس باشه ولی چرا هست؟ :


کد:
private void btnUsing3_Click(object sender, EventArgs e)
        {
            Button a = new Button();
            a.Name = "btnDestructUsing";
            a.Text = "DestructUsing";
            a.BackColor = Color.WhiteSmoke;
            a.Location = new Point(1, 250);
            a.Size = new Size(150, 35);
            this.Controls.Add(a);
            using (Button b = a)
            {
             
            }
            MessageBox.Show(a.Text);
        }
به پاسخ هایی که میدم توجه نمی کنید، using با متد Dispose کار می کنه و فقط اونرو فراخوانی می کنه، ربطی به حافظه شیء نداره. Dispose قراره منابع سیستم که با کد نویسی به شیء تخصیص داده شده رو حذف کنه، کلا آزاد کردن چیزهایی که مدیریت شون با GC نیست، مدیریت حافظه خود شیء که با GC ئه و GC خودش بصورت خودکار هر زمان که لازم باشه حافظه شیء رو آزاد می کنه چه Dispose ای باشه و چه نباشه، آزاد کردن حافظه خود شیء که از اول وظیفه GC ئه و ربطی به Dispose نداره. شما چه شیء تان Disposable باشه و چه نباشه GC حافظه اش رو موقعی که دسترسی ای بهش نمونده پاک می کنه. using نگران حافظه های مدیریت شده NET. که نیست، GC همه شون رو مدیریت می کنه.
این بحث Heap رو هم فراموش کنید، در زبان های سطح بالایی که مدیریت حافظه با برنامه نویس نیست همچین تفاسیری بی معنی ئه. شما در کدتون Heap ندارید و مدیریت حافظه هم با خودتون نیست که بگید متغیرم تو Heap ئه یا جای دیگه ای. برنامه نویس NET. کلا درگیر مسائل داخلی CLR نمیشه. شما کامپایلر نمی نویسید که بریید کدش رو دیباگ کنید و بگید متغیرم الان تو Heap هست یا نیست، GC هیچ ارتباط مستقیمی با کد های شما نداره، فقط زمانی صحبت از Heap مطرح میشه که در کدتون یکجا از کلاس Heap شیء ای ساخته اید.
در کدتون Dispose حقیقتا فراخوانی میشه ولی منابعی که آزاد میشه یا ممکنه آزاد شده باشه ربطی به a.Text نداره. a.Text یک حافظه string عادی ئه و Dispose کاری بهش نداره. در نظر بگیرید که با Dispose کردن متغیر هایی که به اون شیء نشانه رفته بودند null نمیشوند چون شیء سر جاشه، پاک نمیشه. اینکه بعد از Dispose کردن همچنان از شیء استفاده کنید به منطق داخلی کلاسش بستگی داره ولی در حالت کلی ازش اجتناب کنید، مگر اینکه دقیقا بدانید که داخل Dispose چه چیزهایی آزاد میشه و مطمئن باشید که کارکرد عادی شیء بعد از اون Dispose مختل نمیشه.

کد:
        public class CCC : IDisposable
        {
            public void Dispose()
            {
                MessageBox.Show("Dispose");
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var a = new CCC();
            using (var b = a)
            {
            }
            MessageBox.Show("Finish");
        }
و اصولا شما در Dispose حافظه خود شیء رو آزاد نمی کنید، this سر جاشه و اگر بخواهید هم نمی توانید آزادش بکنید. شیء تا وقتی که ارجاعی بهش هست قطعا در حافظه میونه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
به پاسخ هایی که میدم توجه نمی کنید، using با متد Dispose کار می کنه و فقط اونرو فراخوانی می کنه، ربطی به حافظه شیء نداره. Dispose قراره منابع سیستم که با کد نویسی به شیء تخصیص داده شده رو حذف کنه، کلا آزاد کردن چیزهایی که مدیریت شون با GC نیست، مدیریت حافظه خود شیء که با GC ئه و GC خودش بصورت خودکار هر زمان که لازم باشه حافظه شیء رو آزاد می کنه چه Dispose ای باشه و چه نباشه، آزاد کردن حافظه خود شیء که از اول وظیفه GC ئه و ربطی به Dispose نداره. شما چه شیء تان Disposable باشه و چه نباشه GC حافظه اش رو موقعی که دسترسی ای بهش نمونده پاک می کنه. using نگران حافظه های مدیریت شده NET. که نیست، GC همه شون رو مدیریت می کنه.
این بحث Heap رو هم فراموش کنید، در زبان های سطح بالایی که مدیریت حافظه با برنامه نویس نیست همچین تفاسیری بی معنی ئه. شما در کدتون Heap ندارید و مدیریت حافظه هم با خودتون نیست که بگید متغیرم تو Heap ئه یا جای دیگه ای. برنامه نویس NET. کلا درگیر مسائل داخلی CLR نمیشه. شما کامپایلر نمی نویسید که بریید کدش رو دیباگ کنید و بگید متغیرم الان تو Heap هست یا نیست، GC هیچ ارتباط مستقیمی با کد های شما نداره، فقط زمانی صحبت از Heap مطرح میشه که در کدتون یکجا از کلاس Heap شیء ای ساخته اید.
در کدتون Dispose حقیقتا فراخوانی میشه ولی منابعی که آزاد میشه یا ممکنه آزاد شده باشه ربطی به a.Text نداره. a.Text یک حافظه string عادی ئه و Dispose کاری بهش نداره. در نظر بگیرید که با Dispose کردن متغیر هایی که به اون شیء نشانه رفته بودند null نمیشوند چون شیء سر جاشه، پاک نمیشه. اینکه بعد از Dispose کردن همچنان از شیء استفاده کنید به منطق داخلی کلاسش بستگی داره ولی در حالت کلی ازش اجتناب کنید، مگر اینکه دقیقا بدانید که داخل Dispose چه چیزهایی آزاد میشه و مطمئن باشید که کارکرد عادی شیء بعد از اون Dispose مختل نمیشه.

کد:
        public class CCC : IDisposable
        {
            public void Dispose()
            {
                MessageBox.Show("Dispose");
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var a = new CCC();
            using (var b = a)
            {
            }
            MessageBox.Show("Finish");
        }
و اصولا شما در Dispose حافظه خود شیء رو آزاد نمی کنید، this سر جاشه و اگر بخواهید هم نمی توانید آزادش بکنید. شیء تا وقتی که ارجاعی بهش هست قطعا در حافظه میونه.

ممنون استاد علی
توجه دارم
الان شما میگید که حافظه ی هیپ کلا دست gc هست و به ما ربطی نداره و این using هایی که میذاریم (متد dispose رو فراخونی میکنه) یا null هایی که میذاریم در واقع داریم متغییرها رو در حافظه ی استک یا اشاره گرهای اون متغییرها رو در حافظه ی استک مدیریت میکنیم
اما در کدی که دادم (2 پست بالاتر) ، پس چرا وقتی using رو گذاشتم ، شی دکمه مون رو که به فرم اضافه کردم رو کلا پاک کرد؟! در صورتی که using رو اگه برداریم ، دکمه به فرم اضافه میشه. این ، آیا معنی ای جز این میده که using (و متد dispose در کلاس button) داره حتی مقدار شی مون (در حافظه ی هیپ) رو هم از بین میبره پس شی دکمه مون از فرم حذف شد؟ اگه معنی ای بجز این میده ، پس معنی اش چیه؟
ممنون
 

the_king

مدیرکل انجمن
ممنون استاد علی
توجه دارم
الان شما میگید که حافظه ی هیپ کلا دست gc هست و به ما ربطی نداره و این using هایی که میذاریم (متد dispose رو فراخونی میکنه) یا null هایی که میذاریم در واقع داریم متغییرها رو در حافظه ی استک یا اشاره گرهای اون متغییرها رو در حافظه ی استک مدیریت میکنیم
اما در کدی که دادم (2 پست بالاتر) ، پس چرا وقتی using رو گذاشتم ، شی دکمه مون رو که به فرم اضافه کردم رو کلا پاک کرد؟! در صورتی که using رو اگه برداریم ، دکمه به فرم اضافه میشه. این ، آیا معنی ای جز این میده که using (و متد dispose در کلاس button) داره حتی مقدار شی مون (در حافظه ی هیپ) رو هم از بین میبره پس شی دکمه مون از فرم حذف شد؟ اگه معنی ای بجز این میده ، پس معنی اش چیه؟
ممنون
من هیچ حرفی از حافظه Heap یا Stack نزدم که دست کسی باشه، Heap و Stack چیزیه که تو ذهن شما رفته و ازش دست برنمیدارید، از مطالب من هم نیست، نمیدونم منبعش کجا است ولی کلا بی ربط ئه. اینکه در واقع دارید Stack ای رو مدیریت می کنید برداشت شما از مطلبی است که ربطی به گفته های من نداره، هم اشتباهه و هم گفته من نیست. هر ماشین ای اعم از حقیقی و مجازی از Stack استفاده می کنه ولی این ربطی به متغیر های #C نداره. شبیه مباحثی ئه که دانشجو های درس کامپایلر برای کامپایلر دست ساز خودشون بکنند، اصلا با تعاریف و ساختار CLR و #C نمیخونه. وقتی یک متغیر رو null می کنید، صرفا در اشاره گر اش مقدار 0 قرار میگیره، جایی بین صدها متغیر دیگه که در حافظه قرار دارند. تعریف Stack هم خیلی مشخصه و عملیات روش هم محدود به اضافه کردن و حذف کردن مقدار از یک موقعیت خاصه. همیشه مقداری رو میخونه که آخرین مورد نوشته شده است. نه مقدار از وسط خانه ها میخونه و نه می نویسه، با ساختار Stack که نمیشه وسط خانه ها یک مقدار رو صفر کرد یا ازش درآورد، وقتی وسط خانه ها مقدار یک خانه رو عوض کنید یا حذف کنید که دیگه این ساختار Stack نشد. شما ده تا مقدار هم null بکنید اشاره گر شون سر جاشه، هیچ بلایی سرش نمیاد. حافظه اشاره گر آزاد نمیشه و تغییری در ساختار حافظه مربوط به متغیر ها یا طول اون حافظه رخ نمیده. چند بار تاکید کردم که مدیریت حافظه با GC ئه، ولی باز میگید در واقع داریم فلان مورد حافظه رو مدیریت می کنیم.
اما در کدی که دادم (2 پست بالاتر) ، پس چرا وقتی using رو گذاشتم ، شی دکمه مون رو که به فرم اضافه کردم رو کلا پاک کرد؟! در صورتی که using رو اگه برداریم ، دکمه به فرم اضافه میشه. این ، آیا معنی ای جز این میده که using (و متد dispose در کلاس button) داره حتی مقدار شی مون (در حافظه ی هیپ) رو هم از بین میبره پس شی دکمه مون از فرم حذف شد؟ اگه معنی ای بجز این میده ، پس معنی اش چیه؟
ممنون
معنی اش اینه که اون کلاس Button یک پنجره استاندارد ویندوز از کلاس دکمه ساخته که جزو منابع سیستم ئه و Handle پنجره و فونت و ... داره. این منابع مستقیما با API ویندوز در ارتباط اند، فقط یک متغیر حافظه نیستند که تحت کنترل GC باشند و خود شیء مسئول آزاد کردن به موقع اونها است. مثل همون Form ها که Handle استاندارد پنجره ها رو دارند ولی در عین حال کلاس Form ئه NET. یک ساختار مخصوص خودش رو داره.
وقتی این شیء Button قراره Dispose بشه باید تمامی این منابع سیستم مثل اون پنجره ایجاد شده پاک بشه وگرنه یکی از ظرفیت منابع آزاد سیستم کم میشه. Dispose هم اون دکمه استاندارد رو با Handle اش و سایر موارد دیگه که داخل شیء موقع ایجاد شدن دکمه ایجاد شده بود نابود می کنه. شیء از کلاس Button باقی میمونه و پاک نمیشه چون خودش یک شیء NET. ئه و مربوط به این منابع سیستمی نیست.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
من هیچ حرفی از حافظه Heap یا Stack نزدم که دست کسی باشه، Heap و Stack چیزیه که تو ذهن شما رفته و ازش دست برنمیدارید، از مطالب من هم نیست، نمیدونم منبعش کجا است ولی کلا بی ربط ئه. اینکه در واقع دارید Stack ای رو مدیریت می کنید برداشت شما از مطلبی است که ربطی به گفته های من نداره، هم اشتباهه و هم گفته من نیست. هر ماشین ای اعم از حقیقی و مجازی از Stack استفاده می کنه ولی این ربطی به متغیر های #C نداره. شبیه مباحثی ئه که دانشجو های درس کامپایلر برای کامپایلر دست ساز خودشون بکنند، اصلا با تعاریف و ساختار CLR و #C نمیخونه. وقتی یک متغیر رو null می کنید، صرفا در اشاره گر اش مقدار 0 قرار میگیره، جایی بین صدها متغیر دیگه که در حافظه قرار دارند. تعریف Stack هم خیلی مشخصه و عملیات روش هم محدود به اضافه کردن و حذف کردن مقدار از یک موقعیت خاصه. همیشه مقداری رو میخونه که آخرین مورد نوشته شده است. نه مقدار از وسط خانه ها میخونه و نه می نویسه، با ساختار Stack که نمیشه وسط خانه ها یک مقدار رو صفر کرد یا ازش درآورد، وقتی وسط خانه ها مقدار یک خانه رو عوض کنید یا حذف کنید که دیگه این ساختار Stack نشد. شما ده تا مقدار هم null بکنید اشاره گر شون سر جاشه، هیچ بلایی سرش نمیاد. حافظه اشاره گر آزاد نمیشه و تغییری در ساختار حافظه مربوط به متغیر ها یا طول اون حافظه رخ نمیده. چند بار تاکید کردم که مدیریت حافظه با GC ئه، ولی باز میگید در واقع داریم فلان مورد حافظه رو مدیریت می کنیم.
ممنون
این مدیریت حافظه چقدر رو مخ آدمه :green:
چرا؟ مگه هیپ و استک و ساختار متغییرها رو بدونم چه اشکالی داره؟!
من میخوام بیشتر بدونم . یعنی به نظرم یکی از اصول برنامه نویسی یعنی همین ساختار هیپ و استک و نوع مدیریت حافظه
این تیکه که گفتین :

شما ده تا مقدار هم null بکنید اشاره گر شون سر جاشه، هیچ بلایی سرش نمیاد. حافظه اشاره گر آزاد نمیشه و تغییری در ساختار حافظه مربوط به متغیر ها یا طول اون حافظه رخ نمیده. چند بار تاکید کردم که مدیریت حافظه با GC ئه، ولی باز میگید در واقع داریم فلان مورد حافظه رو مدیریت می کنیم.
الان مدیریت استک (متغییر _ نه شی) هم با gc هه فقط و ما نمیتونیم توش دخالت کنیم؟!
ما الان متغییری رو null کنیم ، اشاره گرش رو از دست نمیده توی حافظه ی استک؟!!!! این تیکه رو بیشتر توضیح میدین؟ فرضا ما شی ای از یه کلاس ساختیم ، بعد null اش کردیم . بعد دیگه اعضاش در دسترس نیست و ارور NullReferenceExeption میده. خوب این یعنی اینکه اشاره گرش رو از دست داد دیگه؟! مثل کد زیر :


کد:
private void btnNull1_Click(object sender, EventArgs e)
        {
            Button btnNull = new Button();
            btnNull.Name = "btnNull_2";
            btnNull.Text = "Null_2";
            btnNull.BackColor = Color.WhiteSmoke;
            btnNull.Location = new Point(1, 450);
            btnNull.Size = new Size(150, 35);
            this.Controls.Add(btnNull);

            btnNull = null;
            MessageBox.Show(btnNull.Text);
        }

معنی اش اینه که اون کلاس Button یک پنجره استاندارد ویندوز از کلاس دکمه ساخته که جزو منابع سیستم ئه و Handle پنجره و فونت و ... داره. این منابع مستقیما با API ویندوز در ارتباط اند، فقط یک متغیر حافظه نیستند که تحت کنترل GC باشند و خود شیء مسئول آزاد کردن به موقع اونها است. مثل همون Form ها که Handle استاندارد پنجره ها رو دارند ولی در عین حال کلاس Form ئه NET. یک ساختار مخصوص خودش رو داره.
وقتی این شیء Button قراره Dispose بشه باید تمامی این منابع سیستم مثل اون پنجره ایجاد شده پاک بشه وگرنه یکی از ظرفیت منابع آزاد سیستم کم میشه. Dispose هم اون دکمه استاندارد رو با Handle اش و سایر موارد دیگه که داخل شیء موقع ایجاد شدن دکمه ایجاد شده بود نابود می کنه. شیء از کلاس Button باقی میمونه و پاک نمیشه چون خودش یک شیء NET. ئه و مربوط به این منابع سیستمی نیست.

آها ممنون
 

the_king

مدیرکل انجمن
ممنون
این مدیریت حافظه چقدر رو مخ آدمه :green:
کاملا برعکس، کد نویسی در زبانی که مدیریت حافظه خودکار داره و کاری به برنامه نویس نداره ساده تره، چون نیازی نیست برنامه نویس درگیرش باشه.
چرا؟ مگه هیپ و استک و ساختار متغییرها رو بدونم چه اشکالی داره؟!
من میخوام بیشتر بدونم . یعنی به نظرم یکی از اصول برنامه نویسی یعنی همین ساختار هیپ و استک و نوع مدیریت حافظه
هیچ اشکالی نداره، شاید مفید نباشه ولی دانستن هیچ ایرادی نداره، اما ساختار حافظه رو اونطور که هست ببینید، تخیلی تفسیر نکنید، معادل کدتون رو در زبان ماشین ببینید و تفسیر کنید. در حین Debug کردن برنامه می توانید Disassembly کنید و کد Assembly رو ببینید.
من میخوام بیشتر بدونم . یعنی به نظرم یکی از اصول برنامه نویسی یعنی همین ساختار هیپ و استک و نوع مدیریت حافظه
مدیریت حافظه که البته بحثش کاملا جدا است، کاری که مدیریت حافظه سیستم عامل یا مدیریت حافظه NET. و بازیافت اش در GC هست در ساختار های داده ای Stack و Heap و ... نمی گنجه، کلا مباحث جدایی هستند.
Heap و Stack تا وقتی بصورت کلی بررسی بشوند مربوط به مباحث ساختمان داده اند، مفید و کاربردی، ولی اگه بخواهید به متغیر ها و حافظه اشیاء زبان برنامه نویسی ربطشون بدهید مربوط میشه به مباحثی مثل طراحی کامپایلر.
این کامپایلر ئه که باید متغیر شما رو در جایی ثبت کنه، #C زبان مفسری نیست که سطر به سطر بیاد پایین و تفسیر کنه و بعد ببینه فرضا در این سطر یک متغیر جدید تعریف شده و اون لحظه بفرستتش داخل فرضا یک پشته.
تازه اگر هم مفسری بود باز ربطی به کدی که می نویسید نداره، مباحث طراحی مفسر میشه، نه برنامه نویسی در زبان فلان.
من با یادگیری هیچ چیزی مخالفتی ندارم، راز نهان نیست که کسی رو از یادگرفتنش منع کنیم، مخالفت من سر اشتباه بودن و نامربوط بودن اونها است.
اصول برنامه نویسی در #C یا ++C یا هر زبان دیگه ای واقعا ربطی به اینها نداره. اگر بخواهید یک سیستم عامل یا کامپایلر طراحی کنید قطعا مباحث مفیدی خواهند بود، اما نه برای یادگیری اصول برنامه نویسی، اونم در زبانی که مدیریت حافظه خودکار داره.
الان مدیریت استک (متغییر _ نه شی) هم با gc هه فقط و ما نمیتونیم توش دخالت کنیم؟!
هیچکدوم، نه شما و نه GC دخالتی در اون پشته ندارند. دارید در مورد پشته ماشین مجازی صحبت می کنید، اون پشته که منظورتونه کلا خودش مدیریت خاصی لازم نداره، چون حافظه اش ایجاد نمیشه، حافظه اصلی ماشین مجازی رو داره پر می کنه، یک کلاس نیست، کلا یک دونه است که اونم از اول در ماشین مجازی هست. جزو ساختار ماشین مجازی ئه. همه پردازنده های متعارف اون پشته رو دارند. اون پشته نهایتش دو تا اشاره گر داره که جابجا می شوند، حافظه ای از خودش نداره که بهش بدید و بعد بخواهید پس بگیرید. برای همین GC کاری به پشته ماشین مجازی نداره. دقت کنید که این پشته ربطی به System.Collections.Stack یا کلاس های مشابه نداره چون اونها حافظه آرایه ای دارند که مدیریتش با GC ئه، شما هر بلایی سر اون System.Collections.Stack بیارید باز هم مدیریت حافظه اش با GC ئه و دخالتی درش ندارید.
قاعده کلی اینه که مدیریت حافظه Managed در NET. با GC ئه و برنامه نویس دخالتی داخلش نداره، یعنی هر حافظه ای که از ساختار های داده ای ساده و پیچیده درون NET. تامین بشه مربوط به GC ئه.
حافظه های Unmanaged مثل اون حافظه ای که با System.Runtime.InteropServices.Marshal.AllocHGlobal ساخته میشه دیگه ربط به GC نداره چون خارج از محیط NET. ئه و مستقیما از سیستم عامل دریافت شده و مسئولیت آزاد کردن اش با برنامه نویس ئه. صرفا روی اونها مدیریت دارید.
ما الان متغییری رو null کنیم ، اشاره گرش رو از دست نمیده توی حافظه ی استک؟!!!! این تیکه رو بیشتر توضیح میدین؟ فرضا ما شی ای از یه کلاس ساختیم ، بعد null اش کردیم . بعد دیگه اعضاش در دسترس نیست و ارور NullReferenceExeption میده. خوب این یعنی اینکه اشاره گرش رو از دست داد دیگه؟! مثل کد زیر :
کد:
private void btnNull1_Click(object sender, EventArgs e)
        {
            Button btnNull = new Button();
            btnNull.Name = "btnNull_2";
            btnNull.Text = "Null_2";
            btnNull.BackColor = Color.WhiteSmoke;
            btnNull.Location = new Point(1, 450);
            btnNull.Size = new Size(150, 35);
            this.Controls.Add(btnNull);

            btnNull = null;
            MessageBox.Show(btnNull.Text);
        }
آها ممنون
کدوم Stack؟ از دست رفتن رو به چی تفسیر می کنید؟ حذف شدن از پشته یا تغییر مقدارش به صفر؟
اشاره گر مقدارش رو از دست داد، به عبارتی ساده تر مقدارش به صفر تغییر کرد. نه اینکه اشاره گر از دست بره یا نابود بشه یا اشاره گر از حافظه در بیاد. این تصور رو نداشته باشید که پشته ای هست که چون چهار تا متغیر دارم چهار تا خونه توش مقدار داره و وقتی یک متغیر رو null کردم یک خونه از پشته حذف میشه و وقتی یک متغیر جدید تعریف کردم یک خونه جدید به پشته اضافه میشه.
شما می توانید بعد از اون btnNull = null هر مقدار دیگه ای در btnNull قرار بدید، درسته؟ پس حافظه اشاره گر سر جاشه و ازش گرفته نشده. مقدار داخل اشاره گر ئه که عوض میشه. اینکه اعضاء شیء قابل دسترسی نیست ربطی به خود شیء که نداره؛ شیء سرجاشه، حافظه اش دست نخورده. اون اشاره گر ئه که مقدارش صفر شده و دیگه به شیء اشاره نداره و برای همین خطا رخ میده.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
کاملا برعکس، کد نویسی در زبانی که مدیریت حافظه خودکار داره و کاری به برنامه نویس نداره ساده تره، چون نیازی نیست برنامه نویس درگیرش باشه.

هیچ اشکالی نداره، شاید مفید نباشه ولی دانستن هیچ ایرادی نداره، اما ساختار حافظه رو اونطور که هست ببینید، تخیلی تفسیر نکنید، معادل کدتون رو در زبان ماشین ببینید و تفسیر کنید. در حین Debug کردن برنامه می توانید Disassembly کنید و کد Assembly رو ببینید.

مدیریت حافظه که البته بحثش کاملا جدا است، کاری که مدیریت حافظه سیستم عامل یا مدیریت حافظه NET. و بازیافت اش در GC هست در ساختار های داده ای Stack و Heap و ... نمی گنجه، کلا مباحث جدایی هستند.
Heap و Stack تا وقتی بصورت کلی بررسی بشوند مربوط به مباحث ساختمان داده اند، مفید و کاربردی، ولی اگه بخواهید به متغیر ها و حافظه اشیاء زبان برنامه نویسی ربطشون بدهید مربوط میشه به مباحثی مثل طراحی کامپایلر.
این کامپایلر ئه که باید متغیر شما رو در جایی ثبت کنه، #C زبان مفسری نیست که سطر به سطر بیاد پایین و تفسیر کنه و بعد ببینه فرضا در این سطر یک متغیر جدید تعریف شده و اون لحظه بفرستتش داخل فرضا یک پشته.
تازه اگر هم مفسری بود باز ربطی به کدی که می نویسید نداره، مباحث طراحی مفسر میشه، نه برنامه نویسی در زبان فلان.
من با یادگیری هیچ چیزی مخالفتی ندارم، راز نهان نیست که کسی رو از یادگرفتنش منع کنیم، مخالفت من سر اشتباه بودن و نامربوط بودن اونها است.
اصول برنامه نویسی در #C یا ++C یا هر زبان دیگه ای واقعا ربطی به اینها نداره. اگر بخواهید یک سیستم عامل یا کامپایلر طراحی کنید قطعا مباحث مفیدی خواهند بود، اما نه برای یادگیری اصول برنامه نویسی، اونم در زبانی که مدیریت حافظه خودکار داره.

هیچکدوم، نه شما و نه GC دخالتی در اون پشته ندارند. دارید در مورد پشته ماشین مجازی صحبت می کنید، اون پشته که منظورتونه کلا خودش مدیریت خاصی لازم نداره، چون حافظه اش ایجاد نمیشه، حافظه اصلی ماشین مجازی رو داره پر می کنه، یک کلاس نیست، کلا یک دونه است که اونم از اول در ماشین مجازی هست. جزو ساختار ماشین مجازی ئه. همه پردازنده های متعارف اون پشته رو دارند. اون پشته نهایتش دو تا اشاره گر داره که جابجا می شوند، حافظه ای از خودش نداره که بهش بدید و بعد بخواهید پس بگیرید. برای همین GC کاری به پشته ماشین مجازی نداره. دقت کنید که این پشته ربطی به System.Collections.Stack یا کلاس های مشابه نداره چون اونها حافظه آرایه ای دارند که مدیریتش با GC ئه، شما هر بلایی سر اون System.Collections.Stack بیارید باز هم مدیریت حافظه اش با GC ئه و دخالتی درش ندارید.
قاعده کلی اینه که مدیریت حافظه Managed در NET. با GC ئه و برنامه نویس دخالتی داخلش نداره، یعنی هر حافظه ای که از ساختار های داده ای ساده و پیچیده درون NET. تامین بشه مربوط به GC ئه.
حافظه های Unmanaged مثل اون حافظه ای که با System.Runtime.InteropServices.Marshal.AllocHGlobal ساخته میشه دیگه ربط به GC نداره چون خارج از محیط NET. ئه و مستقیما از سیستم عامل دریافت شده و مسئولیت آزاد کردن اش با برنامه نویس ئه. صرفا روی اونها مدیریت دارید.

ممنون استاد علی
"معادل کدتون رو در زبان ماشین ببینید" یعنی چی؟ متوجه نشدم
"در حین Debug کردن برنامه می توانید Disassembly کنید و کد Assembly رو ببینید" هم یعنی چی؟ بازم متوجه نشدم


کدوم Stack؟ از دست رفتن رو به چی تفسیر می کنید؟ حذف شدن از پشته یا تغییر مقدارش به صفر؟
اشاره گر مقدارش رو از دست داد، به عبارتی ساده تر مقدارش به صفر تغییر کرد. نه اینکه اشاره گر از دست بره یا نابود بشه یا اشاره گر از حافظه در بیاد. این تصور رو نداشته باشید که پشته ای هست که چون چهار تا متغیر دارم چهار تا خونه توش مقدار داره و وقتی یک متغیر رو null کردم یک خونه از پشته حذف میشه و وقتی یک متغیر جدید تعریف کردم یک خونه جدید به پشته اضافه میشه.
شما می توانید بعد از اون btnNull = null هر مقدار دیگه ای در btnNull قرار بدید، درسته؟ پس حافظه اشاره گر سر جاشه و ازش گرفته نشده. مقدار داخل اشاره گر ئه که عوض میشه. اینکه اعضاء شیء قابل دسترسی نیست ربطی به خود شیء که نداره؛ شیء سرجاشه، حافظه اش دست نخورده. اون اشاره گر ئه که مقدارش صفر شده و دیگه به شیء اشاره نداره و برای همین خطا رخ میده.

آها ممنون
پس کلا اصلا خود متغییرهایی که در استک هستند مثل کد :
کد:
Button btnNull = new Button();
که در بالا دادم ، اصلا به هیچ عنوان از استک پاک نمیشن؟ فقط مقدار این اشاره گرشون عوض میشه؟
بعد اینکه همونطور که قبلا یه لینک دادم ، درمقاله در لینک زیر :
زنگ سی شارپ – قسمت بیست و هفتم | webtarget
در پاراگراف سوم در سه خط مونده به آخر گفته :
"در نهایت هنگامی‌که برنامه به انتهای متد می‌رسد، متغیرهایی که در stack بودند همه‌گی پاک می‌شوند"
منظورش اینه مقدار اشاره گر متغییرها صفر میشن از توی استک یا خود متغییرها پاک میشن؟ به طبع ، طبق توضیحات تون باید منظورش صفر شدن مقدار اشاره گر متغییر بوده باشه دیگه؟
-----------
بعد اینکه هر اشاره گر که عدد هست ، چند بیتی هست؟ این بیت ها مقدار ثابتی برای همه ی متغییرها دارن؟ یعنی الان متغییر btnNull ، مقدار اشاره گرش چند بیتتی هست؟ قبلا گفته بودین که 32 یا 64 بیتی هستن . الان این متغییر چند بیتی هست و از کجا متوجه میشین؟
 
آخرین ویرایش:

the_king

مدیرکل انجمن
ممنون استاد علی
"معادل کدتون رو در زبان ماشین ببینید" یعنی چی؟ متوجه نشدم
"در حین Debug کردن برنامه می توانید Disassembly کنید و کد Assembly رو ببینید" هم یعنی چی؟ بازم متوجه نشدم
Disas.png

وقتی در کد تون جایی Breakpoint قرار بدید و اجرای کد در حالت Debug متوقف بشه می توانید معادل اسمبلی کد تون رو ببینید.
Disas2.png

کاربردش محدوده ولی واقعیت رو نشون میده.

آها ممنون
پس کلا اصلا خود متغییرهایی که در استک هستند مثل کد :
کد:
Button btnNull = new Button();
که در بالا دادم ، اصلا به هیچ عنوان از استک پاک نمیشن؟ فقط مقدار این اشاره گرشون عوض میشه؟
بعد اینکه همونطور که قبلا یه لینک دادم ، درمقاله در لینک زیر :
زنگ سی شارپ – قسمت بیست و هفتم | webtarget
در پاراگراف سوم در سه خط مونده به آخر گفته :
"در نهایت هنگامی‌که برنامه به انتهای متد می‌رسد، متغیرهایی که در stack بودند همه‌گی پاک می‌شوند"
منظورش اینه مقدار اشاره گر متغییرها صفر میشن از توی استک یا خود متغییرها پاک میشن؟ به طبع ، طبق توضیحات تون باید منظورش صفر شدن مقدار اشاره گر متغییر بوده باشه دیگه؟
شما اول فرض مساله رو مستدل کنید بعد برسید به سوال پاک شدن و نشدنش. شما اول فرض رو بر این قرار داده اید که متغیر هاتون در پشته قرار دارند. در حالی که کامپایلر برای هر کدوم یک آدرس مجزا در نظر گرفته، با push و pop که متغیر در حافظه قرار نداده :
Disas3.png

منظورش رو از خود نویسنده اش باید بپرسید اما متغیر محلی خارج از محیط متد وجود نداره که بخواد صفر بشه یا پاک بشه. یک آدرس حافظه داخل متد مربوط به اون متغیر بوده، خارج از محیط متد دیگه نه اون متغیر هست و نه اون آدرس به اون متغیر مربوطه. موقع بازگشت از متد هم نه کدی برای null کردن اشاره گر ها هست و نه خارج کردن متغیر ها از یک پشته. این تفسیر های تخیلی رو فراموش کنید.
بعد اینکه هر اشاره گر که عدد هست ، چند بیتی هست؟ این بیت ها مقدار ثابتی برای همه ی متغییرها دارن؟ یعنی الان متغییر btnNull ، مقدار اشاره گرش چند بیتتی هست؟ قبلا گفته بودین که 32 یا 64 بیتی هستن . الان این متغییر چند بیتی هست و از کجا متوجه میشین؟
اینکه 32 بیتی ئه یا 64 بیتی به نوع Build پروژه و محیطی که اجرا میشه بستگی داره. در تنظیمات Build پروژه می توانید صریحا روی x86 یا x64 تنظیم اش کنید. اگه میخواهید در کد نویسی متوجه بشوید راحت ترین راه حلش IntPtr.Size ئه که 4 یا 8 بایتی ئه. از روی آدرس های حافظه Assembly هم مشخصه، آدرس های 32 بیتی 8 حرفی اند و آدرس های 64 بیتی 16 حرفی اند مثلا آدرس حافظه 00232F45 طولش 8 حرف ئه و 32 بیتی ئه.
 

SajjadKhati

کاربر فعال <A href="http://forum.majidonline.com/f
ممنون
استاد علی ، در کد زیر :

کد:
private void btnArrayCopy7_Click(object sender, EventArgs e)
        {
            StringBuilder[] a = new StringBuilder[] { new StringBuilder("salam"), new StringBuilder("khodahafez") };
            StringBuilder[] b = new StringBuilder[a.Length] ;


            a[0][0] = 'c';
            MessageBox.Show("a is : " + a[0] + "\nb is : " + b[0]);
        }

در خط سوم که فضای خالی گذاشته شد ، آسون ترین کدی که بشه نوشت تا آرایه ی a رو توی آرایه ی b کپی کرد ، جوری که با اجرای خط بعدی (خط یکی مونده به آخری) ، مقدار هر دو عوض نشه ، چیه؟
فقط این راه هست که توی دو حلقه ی تو در تو که اولین حلقه به تعداد اعضای آرایه و دومین حلقه به تعداد کاراکترهای StringBuilder اجرا شه و تک تک کاراکترهای استرینگ بیلدر در اعضای آرایه ی a رو توی تک تک کاراکترهای اعضای StringBiulder ای که برای آرایه ی b در نظر میگیریم ، بریزیم؟
تابع راحت تری برای این کار وجود نداره که نه بصورت اشاره گر بلکه در دو حافظه ی مختلف ، هر نوع عضو آرایه (حتی عضو آرایه ای که مثل این شی ای از کلاس و اشاره گر هستند) رو کپی کنه؟ تابع Array.Copy جواب نداد
یعنی جوابش فقط این راه زیر میشه و راه دیگه ای نداره؟ :


کد:
private void btnArrayCopy7_Click(object sender, EventArgs e)
        {
            StringBuilder[] a = new StringBuilder[] { new StringBuilder("salam"), new StringBuilder("khodahafez") };
            StringBuilder[] b = new StringBuilder[a.Length];

            for (int arrayLength = 0; arrayLength < a.Length; arrayLength++)
            {
                b[arrayLength] = new StringBuilder();
                b[arrayLength].Length = a[arrayLength].Length;

                for (int elementLength = 0; elementLength < a[arrayLength].Length; elementLength++)
                {
                    b[arrayLength][elementLength] = a[arrayLength][elementLength];
                }
            }
              
            a[0][0] = 'c';
            MessageBox.Show("a is : " + a[0] + "\nb is : " + b[0]);
        }
 
آخرین ویرایش:

the_king

مدیرکل انجمن
ممنون
استاد علی ، در کد زیر :

کد:
private void btnArrayCopy7_Click(object sender, EventArgs e)
        {
            StringBuilder[] a = new StringBuilder[] { new StringBuilder("salam"), new StringBuilder("khodahafez") };
            StringBuilder[] b = new StringBuilder[a.Length] ;


            a[0][0] = 'c';
            MessageBox.Show("a is : " + a[0] + "\nb is : " + b[0]);
        }

در خط سوم که فضای خالی گذاشته شد ، آسون ترین کدی که بشه نوشت تا آرایه ی a رو توی آرایه ی b کپی کرد ، جوری که با اجرای خط بعدی (خط یکی مونده به آخری) ، مقدار هر دو عوض نشه ، چیه؟
فقط این راه هست که توی دو حلقه ی تو در تو که اولین حلقه به تعداد اعضای آرایه و دومین حلقه به تعداد کاراکترهای StringBuilder اجرا شه و تک تک کاراکترهای استرینگ بیلدر در اعضای آرایه ی a رو توی تک تک کاراکترهای اعضای StringBiulder ای که برای آرایه ی b در نظر میگیریم ، بریزیم؟
تابع راحت تری برای این کار وجود نداره که نه بصورت اشاره گر بلکه در دو حافظه ی مختلف ، هر نوع عضو آرایه (حتی عضو آرایه ای که مثل این شی ای از کلاس و اشاره گر هستند) رو کپی کنه؟ تابع Array.Copy جواب نداد
یعنی جوابش فقط این راه زیر میشه و راه دیگه ای نداره؟ :


کد:
private void btnArrayCopy7_Click(object sender, EventArgs e)
        {
            StringBuilder[] a = new StringBuilder[] { new StringBuilder("salam"), new StringBuilder("khodahafez") };
            StringBuilder[] b = new StringBuilder[a.Length];

            for (int arrayLength = 0; arrayLength < a.Length; arrayLength++)
            {
                b[arrayLength] = new StringBuilder();
                b[arrayLength].Length = a[arrayLength].Length;

                for (int elementLength = 0; elementLength < a[arrayLength].Length; elementLength++)
                {
                    b[arrayLength][elementLength] = a[arrayLength][elementLength];
                }
            }
             
            a[0][0] = 'c';
            MessageBox.Show("a is : " + a[0] + "\nb is : " + b[0]);
        }
بعضی کلاس ها Clone دارند که StringBuilder جزو اونها نیست، پس سربار ایجاد کلاس جدید کمتر از ایجاد دستی شیء جدید با new نمیشه، آرایه ها Clone دارند ولی اینجا کاربردی نداره چون اشاره گر رو Clone می کنه و نه مقدار StringBuilder رو و در نتیجه هر دو اشیاء مشابه میشوند. ولی به هر حال وقتی خود StringBuilder رو می توانید یکجا بسازید، چرا با آرایه تک به تک خونه هاش رو پر می کنیم :
کد:
            var a = new StringBuilder[] { new StringBuilder("salam"), new StringBuilder("khodahafez") };
            var b = new StringBuilder[a.Length];
            for (int arrayLength = 0; arrayLength < a.Length; arrayLength++)
            {
                b[arrayLength] = new StringBuilder(a[arrayLength].ToString());
            }

            a[0][0] = 'c';
            MessageBox.Show("a is : " + a[0] + "\nb is : " + b[0]);
 

جدیدترین ارسال ها

بالا