شرایط مسابقه (Race Condition) چیست و چرا !؟

سلام خدمت همه گرامیان و عزیزان که مجموعه مقالات من رو در زمینه EntityFramework Core تا الان دنبال کردن. میدونم خدمتتون قول داده بودم که در ادامه Eager loading براتون توضیحات روش جادویی Lazy Loading رو شرح بدم ولی خب احساس کردم دوپا شیرجه رفتن تو دل این مطلب بدون آشنایی با مقدمات و اصطلاحات کار اشتباهیه ? این شد که تصمیم گرفتم از یه جای تقریبا مناسب که Race Condition باشه شروع کنم به توضیح مطالب تا ایشالا در نهایت برسیم به Lazy Loading ??.برای شروع باید بگم که Race Condition یا Race Hazard شرایطی هست که یک مدار الکترونیکی(منطقی) یا نرم افزار کامپیوتری یا یک سیستم پیچیده رفتار و کارکردی داره که مشخصا متکی به توالی زمانی رخ داد رویداد های غیر قابل کنترل هست.رویداد غیر قابل کنترل یعنی رویدادی که زمان اتفاق افتادنش برای ما نامعلوم هست و باید برای پذیرش این درخواست یا اتفاق هرلحظه آماده باشیم ?معمولا در شرایط مسابقه ای الویت زمانی این رویداد ها برای ما مهمه ? مثلا نرم افزار یک برنامه تلویزیونی رو در نظر بگیرید که هر کس بعد از خوندن سوال توسط مجری زودتر زنگ رو بزنه برای ما مهم تر میشه.احتمالا دارید به این فکر میکنید که ممکنه دو نفر همزمان زنگ رو فشار بدن و دردسر درست بشه ?، بله متاسفانه همینطوره و اگر یک یا چند تا رخ داد برای ما غیر منتظره باشه و نتونیم درست بهشون رسیدگی کنیم, شرایط مسابقه میتونه به راحتی تبدیل به یک باگ بزرگ بشه برای ما ?!اگر دانش آموخته رشته کامپیوتر یا الکترونیک باشید احتمالا این اطلاعات رو قبلا خوندید و براتون آشناست ?! در مدار های منطقی یا سیستم های توزیع شده و یا دارای چند رشته پردازشی که ممکنه چند رویداد همزمان در رشته های مختلف رخ بدن به طور معمول شرایط مسابقه خود به خود پدید میاد !لفظ Race Condition سابقه طولانی در علوم کامپیوتر و الکترونیک داره و حتی از سال 1954 در رساله دکترای دانشمند معروف ? David A. Huffman به نام "The synthesis of sequential switching circuits" هم دیده شده.ما در این مقاله قسمت مربوط به شرایط مسابقه در الکترونیک و مدار های منطقی رو توضیح نمیدیم ولی اگر خودتون خیلی علاقه مند هستید می تونید از منابعی که پایین معرفی کردم دنبال کنید. و اما در توضیح Race Condition در طراحی نرم افزار های کامپیوتری باید بگم این شرایط زمانی رخ میده که یک نرم افزار در کارکردش به توالی و رخ داد زمانی چند تا Thread یا Process متکی و حساس باشه ?!از بین تمام شرایط مسابقه ای که ممکنه در نرم افزار ها رخ بده، بعضی از اون ها وجود دارن که واقعا بحران آفرین هستن(Critical Race Condition) و پتانسیل به فنا دادن برنامه شما و حتی فراتر از اون، جون شما رو دارن ??!همونطور که می دونید هر برنامه ای که از چندین رشته پردازشی تشکیل شده باشه، در حافظه Ram مربوط به برنامه قسمت های حافظه ای مخصوص اطلاعات هر رشته وجود داره و رشته ها در حالت معمول نمیتونن به اطلاعات بقیه رشته ها دسترسی داشته باشن و این در اکثر اوقات مزیت خوبی حساب میشه و برنامه رو از مشکلات همزمانی پردازش ها نجات میده ?! ولی نکته مهم اینجاست که معمولا قسمتی از حافظه وجود داره به نام Shared Memory که اطلاعات سراسری یا همرسانی شده بین رشته ها در اون نگه داری میشه و همه رشته ها قابلیت دسترسی و تغییر این قسمت از رم رو دارن ? و این خیلی ترسناکه وقتی با Race Condition سر و کار داریم. مثلا متغییر های جهانی و سراسری (Global) یا اطلاعات درون هر کلاسی که به صورت استاتیک تعریف شده باشه در این قسمت حافظه جای دارن ?.علاوه بر موضوع حافظه مشترک، این اختلال بین پردازش های مختلف در دسترسی به بعضی از Resource ها(منابع کامپیوتری مثل فایل ها، پرینتر و دی وی دی رایتر) که قابلیت انجام بیش از یک کار همزمان رو ندارن هم ممکنه رخ بده! برای مثال یه پرینتر رو در نظر بگیرید که اگر تا تمام شدن پرینت یک صفحه اطلاعات صفحه جدیدی برای چاپ بفرستید قاطی کنه و روی همدیگه صفحات رو چاپ کنه ?. پس بعضی از منابع هم به صورت مشترک بین پردازش های همزمان قابل دسترس هستن.به این حالت های مشترک بین رشته های پردازشی Shared State میگن !خب مشخصا Race Condition در نرم افزار های چند رشته ای و دارای پردازش همزمان ممکنه وجود داشته باشه ولی در حالتی که هر رشته فقط در محدوده خودش پردازش کنه و به هیچ Shared State ای متکی نباشه همه چی میتونه اوکی باشه ??! ولی امان از روزی که شما یه Race Condition داشته باشید، به صورت خواسته یا ناخواسته چند رشته پردازشی از اون با یک یا چند Shared State سر و کار داشته باشن !!! اینجاعه که اوضاع قاطی پاتی میشه و اگر محافظت نکنید احتمال اینکه رشته ها باعث ایجاد نتایج نامطلوب بشن بسیار بالا میره ??☹️?!به این مدل شرایط مسابقه خطرناک و بحرانی Critical Race Condition میگن!در علوم کامپیوتر نقل شده که برای جلوگیری از این مشکلات عملیات هایی که بر روی Shared State ها انجام میشن(مثل ارسال یه صفحه به پرینتر یا تغییر مقدار یه متغیر سراسری یا استاتیک) Critical Section نام دارن و باید سعی کنید ازشون کمتر استفاده کنید و اگر هم استفاده کردید باید به شکل Mutual exclusion باشن ! در واقع مفهموم دو به دو منفرد بودن یا (Mutual exclusion) که Mutex هم بهش میگن در علوم کامپیوتر به این شکل هست که نباید دو پردازش اجازه داشته باشن همزمان از یه Shared State استفاده کنن! شما باید مکانیزمی رو طراحی کنید که اگر یک پردازش شروع به استفاده یا تغییر یه Shared State کرد بقیه پردازش ها با اینکه رو یه رشته دیگه هستن ولی صبر کنن تا استفاده رشته اول از Shared State تموم بشه و اگر بعد از تموم شدن کار رشته اول، رشته دیگه ای کار خاصی با اون Shared State داشت وارد فرآیند بشه ولی همزمان باهم هرگز! در واقع یه جور لیست برای دسترسی به Shared State ها بین رشته ها ایجاد کنید ??! اینکار معمولا با استفاده از متغیر نگهبانی اتفاق میافته که خودش در Shared State هست و مشخص میکنه که یه Shared State دیگه الان در حال استفاده هست یا نه که به این متغییر Semaphore گفته میشه و در این عملیات اصطلاحا ما اون Shared State رو Lock کردیم و بعد از اتمام پردازش رشته اول Unlock کردیم که توضیحات مخصوص خودش رو داره!حالا داستان برای تعریف کردن زیاده ولی حتما توصیه میکنم چند ساعتی وقت بزارید تو یه زمان خالی از زندگیتون و خودتون مطالب مربوط به Critical Section ،Mutex و Semaphore رو مطالعه کنید و نحوه پیاده سازیشون رو یاد بگیرید هرچند که خودم هم بلد نیستم کامل و فقط مجبور شدم چند باری سریع مطالعشون کنم ?. همچنین یه مفهوم جالب دیگه به اسم Data Race هم وجود داره که نوعی از Race Condition ها حساب میشه و توصیه میکنم اونم دنبال کنید مخصوصا اگر با C و ++C سر و کار دارید.یه نکته همینجا بگم تا یادم نرفته! اگر یک برنامه نویس با تجربه باشید قطعا تا حالا با باگ های هایزنبرگ(Heisenbug) برخورد داشتید و حساب کار دستتون اومده که چه جوونور هایی هستن! اگر هم نمی دونید بگم که باگ Heisenbug در واقع مشکل نرم افزاریه که به محض اینکه برنامه نویس تلاش میکنه تا در حالت Debug Mode یا همون عیب یابی خودمون این مشکل رو بررسی و حل کنه خود مشکل غیب میشه ??! و به این راحتی در حالت غیر از عرضه رسمی محصول(Production) قابل بررسی نیست! متاسفانه معمولا اگر خطای طراحی نرم افزاری رو مرتکب بشید در استفاده از Critical Race Condition این خطا در برنامه باگ هایزنبرگ خواهد بود و فقط در صورتی که تعداد پردازش ها بالا بره در حالت Production شما نتیجه به فنا رفتن رو شاهد خواهید بود ? و در حالت دیباگ متوجه نمیشید مشکل از کدوم قسمت کد شماست! علت هم معمولا اینکه نتیجه اجرای کد قابل پیشبینی نیست و همه چی به زمان اجرای بین رشته های پردازشی بستگی داره.بیایید براتون یه مثال از همین Critical Race Condition بزنم و نتیجه خفت بارش رو از نزدیک ببینید ?! فرض بکنید که دو تا Thread داریم که هر دو میخوان مقدار یه متغییر سراسری(Global) رو همزمان افزایش بدن، در نتیحه ما میل داریم که اتفاقات پایین به ترتیب رخ بدن : ولی نکته اینکه اگر این رشته های پردازشی قرار باشه همزمان اجرا بشن بدون محافظت و لاک شدن نتیجه ممکنه کاملا اشتباه باشه! به این ترتیب :همونطور که میبینید جواب باید 2 باشه بعد از دوبار افزایش ولی همچنان 1 هست ! علت اینکه این دو پردازش Mutually exclusive نیستن و یکی در حین کار اون یکی مزاحمش میشه و مقدار اشتباهی رو میگیره! این از من به شما نصیحت که :نگذارید پردازش هاتون مزاحم همدیگه بشن زمانی که جداگانه مشغول کارن ??!چند تا نکته مهم دیگه هم بگم و رفع زحمت کنم از خدمتتون! تعدادی از باگ های مهم امنیتی که میتونن باعث ورود غیر مجاز به سرویس شما بشن و یا حتی کل سرویس رو بخوابونن از همین طریق Critical Race Condition بوجود میان! برای مثال در حملات سایبری معروف Dos و DDos مهاجم با ارسال تعداد بالایی درخواست به سیستم شما در بخشی از حمله قصد داره باعث بالارفتن تعداد پردازش ها در سیستم بشه و در نتیجه در جایی که شما سوتی داده باشید یک باگ Race Condition از نرم افزار بزنه بیرون و ناک اوت ?!!! و یا حتی مجموعه باگ های مهمی مثل Privilege escalation(تشدید اختیارات و دسترسی) که در یک فقره از این مجموعه مثل Buffer overflow مهاجم میتونه با درخواست پیاپی برای نوشت بر روی قسمتی از حافظه به قسمت های دیگه هم دسترسی بگیره و اطلاعات برنامه ها یا افراد دیگه رو هم بخونه ?! باگ عجیبی مثل time-of-check-to-time-of-use TOCTTOU هم ممکنه در سرویس های Auth شما اتفاق بیافته که نتیجه خیلی دردناک میشه ?!از اونجایی که فایل ها و همینطور شبکه سیستم هم معمولا به صورت Shared State در نرم افزار ها در دسترس رشته های پردازشی هستن باید با روش هایی مثل لاک کردنشون موقع استفاده ازشون در برابر برنامه خودتون ? محافظت کنید!چیز خیلی جالبی که متاسفانه باید بهش اشاره کنم: در سیستم هایی که با جون آدمیزاد سر و کار دارید باید حواستون رو خیلی به همین Critical Race condition ها جمع کنید چون قبلا سابقه کشته دادن داشته ???! دستگاه رادیوتراپی هسته ای Therac-25 که توسط اتحادیه اروپا و فرانسه وارد بازار شده بود! در فاصله سال های 1985 تا 1987 بیش از 6 حادثه آفرید !! این مشکلات در نتیجه یک باگ Critical Race condition بود که مقدار دوز بسیار بالاتری از حد نرمال مواد هسته ای به بدن بیمار وارد میکرد ??☠️! بعدا مشخص شد در نتیجه این حوادث مستقیما 3 بیمار فوت شدن و چندین تن هم صدمات سهمگینی دیدن ?! شاید باورتون نشه ولی در سال 2003 هم در سیستم های مدیریت انرژی شرکت جنرال الکتریک آمریکا، در زیر سیستم مربوط به آلارم اضطراری یک باگ Race Condition وجود داشته که باعث میشده در صورتی که 3 تا از خط های تولید برق همزمان دچار مشکل بشن و بخوان آلارم رو روشن کنن در نتیجه سه درخواست همزمان روشن شدن آلارم، سیستم قاطی کنه و کلا آلارم روشن نشه ?! در نتیجه مشکل شبکه برق از چشم کارمند ها دور بمونه و فاجعه قطعی سراسری برق آمریکا شمالی در سال 2003 رخ بده ?! در این حادثه برق قسمت های وسیعی از کانادا و آمریکا قطع میشه و حتی بعضا تا 3 روز بعد هم وصل نمیشه.خلاصه که دوستان برنامه نویس گلم! این داستان خطرناکه و از خودتون و کدهاتون در مقابل شرایط مسابقه بحرانی مراقبت کنید که خدایی نکرده بحران آفرینی نکنید ?! در تهییه محتوای این مقاله و ترتیب قرار گیری بخش های مختلف توضیحات سعی شده از صفحه رسمی ویکیپدیا انگلیسی Race Condition استفاده بشه. از همینجا از همه نویسندگان آزاد ویکیپدیا در سراسر جهان تشکر میکنم که اطلاعات خودشون رو با ما به اشتراک گذاشتن، میذارن و خواهند گذاشت ?!در صورتی که محتوای این مقاله رو دوست داشتید و لذت بردید من رو در توییتر و ویرگول فالو کنید تا از ادامه آموزش Lazy Loading مطلع بشید و با همین فرمون باهم بریم جلو ???.مهر 1398 محمد احسان طباخیان ❤️