السلام عليكم
من المعلوم الان ان العالم الحاسوبى يتجه نحو المعالجة المتوازية المخالفة بالطبع لأنظمة المعالجة التقليدية ، دليل على ذلك معمارية المعالج ثنائى النواة Core 2 Due architecture التى طرحت من قبل الشركات الكبيرة مثل انتل ،وصراحة الاسواق الان فى حالة إحلال بهذه التقنيات حتى اننا سمعنا عن رباعية النواة..وبالتالى نحن كمطورين ملزمين بالطرق البرمجية التى تحقق اقصى استفادة من تلكم المعماريات الجديدة ..وإلا فإننا بالضبط كمن استعان برافعة عملاقة لتحمل بضع القوالب من الطوب !!! ..
المهم كانت تلك المقدمة لتحفذنا على مراعاه الجديد فى عالم البرمجة والكمبيوتر ...ومن هذا الجديد القديم أيضا ومعروف ألا وهى البرمجة المتوازية Parallel programming
كمطورين دوت نت لدينا مايعرف بالبرمجة متعددة الخيوط (ومعذرة على استخدامى مصطلحات معربة ثقيلة نوعا ما لكن سأكتب الاسم العلمى المشهور حفاظا على العلم ونسبه لأهله ..للأسف)Multithreading programming...ومن جرب استعمال هذه التقنية سواءا على منصات الدوت نت او منصات الجافا او خلافه ..فهو يعلم ثقلها ..وصعوبة الفهم احيانا تقف عائق ..وايضا عدم دقة النتائج ...لكنه احيانا يكون مضطر ..والعاملون على برامج تتعامل بكثافة مع قواعد البيانات او بتقنيةالخادم العميل قد اضطروا كثيرا للهروب اليها ...
معذرة على الاطالة لكن كانت لابد للدخول فى صلب الموضوع ألا وهو مشكلة Race Conditionولمن لايعرف هذه المشكلة فهى ببساطة تسابق متزامن لعمليتين كلاهما يؤدى نفس الغرض ..احدهما قد يأتى قبل الاخر اعتمادا على حدث Event معين او تزامن ما ..وهى عشوائية بحتة ...(نظريا هى تشبه مشكلة الـ DeadLock) ..لكن ما الذى يعنينى كمبرمج من هذا الصداع ؟؟!! ....الاجابة فتش عنها فى الاعلى اذا كنت مهتم لما قلت ..لكن دع الكود يتكلم عنى ...مستعملا VS2008 واطار الدوت نت 3.5 ..مع ملاحظة الترقية التالية للـParallel Extention .وإلا فالعمل على .NET 2.0 قد تضطر لعملSnippest للكلاس Parallel الى جانب التعامل مع الـDelegate بالطريقة المعتادة !
class RaceCondition
{
public static int Total = 0;
public void Test(string myString)
{
// Do something with myString:
Total++; :
}
} }الكود واضح عبارة عن كلاس به دالة سنقوم بمناداتها لحساب عدد مرات تكرار النص Total فيها عن طريق عدد من الـThreads فى الكود التالى ...>
// بالطيع تنبه اسفل الى الـ Lambda Expression
int LoopsNo = 200000;
int MultNo=4;
var test =new RaceCondition() //تنبه للـvar ممكن استعمال الكلاس Test لكن فقط هنا انا ابرز الجديد فى .NET 3.5
Parallel.For(0, MultNo, (taskNo) =>
{
for (int i = 0; i < LoopsNo; i++)
{
RaceCondition.test("Message " + i.ToString());
}
});
Console.WriteLine("{0} Expected", LoopsNo* MultNo);
Console.WriteLine("{0} Actual", RaceCondition.Total);طبعا ناد على الدالة سواءا من داخل الكلاس او بكلاس منفصل لا يهم ..ركز فى الآت ....بالطبع أحدهم يصرخ ماهذه التفاهة هل كل هذا من أجل حساب 4*200000 ؟!!
طبعا :) ....لااااا :wink: ..النتيجة المتوقعة هى 800000 اليس كذلك ؟؟ ....خطأ ...النتيجة لن تظهر كذلك إلا مصادفة ..اذا ما الاجابة الصحيحة ؟؟ الاجابة الصحيحة غير معروفة ومتغيرة دائما على حسب الـtiming او اى حدث Event آخر غير معلوم ...فمثلا النتيجة اول ماظهرت ظهرت كالتالى :
800000 Expected
736354 Actual
وهكذا كلما اعدت تشغيل البرنامج سيظهر نتيجة مختلفة تماما .........بالطبع لا تلعن المترجم الخاص بالدوت نت او تلعن ميكروسوفت ..الكود صحيح والمترجم صحيح ..اذا ماذا حدث؟؟!!.... السر يكمن فى المعالجة متعددة الخيوط MultiThreading ، لمن لم يتعود على هذا النوع ، وبالطبع أخذ فى اعتباره عندما قام هو بتحليل الكود ان العبارة Total++ ماهى الا مجرد سطر برمجى واحد ينفذ مرة واحدة ...هذا للمبرمج الطبيعى الكود ماهو الا single thread process ......لكن فى الحقيقة الذى حدث هو ان تمت معالجتها على انها اربع تعليمات وليس تعليمة برمجية واحدة !!!! ..الم اقل لك ان المعالجة متعددة الخيوط مربكة للدماغ ؟؟!! :mrgreen: وحتى تصدقنى ..قم بعمل ترجمة عكسية لهذه العبارة فقط (كالمعتاد) الى IL لتشاهد معى هذا
L_0001: ldsfld int32 CSharpx.RaceCondition::Total L_0006: ldc.i4.1 L_0007: add L_0008: stsfld int32 CSharpx.RaceCondition::Total
وهو ما معناه الاتى :
1-احسب القيمة الحالية للمتغير Total
2-حصل المترجم على القيمة 1 //تذكر ++ تعنى زيادة بمقدار واحد
3-اجمع القيمتين السابقتين Total +1 //بالنسبة للمرة الواحدة وبالطبع قيمة Total تتغير
4-خزن الناتج فى المتغير Total
هذا بالطبع بالنسبة للمعالجة التقليدية ،لكن ماذا لو تم معالجتها على اعتبار انها عمليتين 2 threads ??!! هل ياترى ستبقى النتيجة كما هى ؟؟!! لنر .....>
[Thread 1] 1. Get the current value of Total (gets 2)
[Thread 1] 2. Get the value 1
[Thread 2] 1. Get the current value of Total (gets 2)
[Thread 2] 2. Get the value 1
[Thread 1] 3. Add the two numbers together (2 + 1)
[Thread 1] 4. Save the result back into Total (saves 3)
[Thread 2] 3. Add the two numbers together (2 + 1)
[Thread 2] 4. Save the result back into Total(saves 3)
بالطبع سنتفترض ان قيمة الـTotal حاليا =2 ..وذلك لحجز القيمتين لزوج العمليات 2 threads ...
وأخيرا بعد صداع الراس المؤلم سيتضح العيب الذى ندندن حوله من الصبح :!: ...هل لاحظت ان قيمة Total لم تزداد إلا بمقدار 1 ؟؟!! تذكر ان كلا القيمتين الحاليتين من زوج العميات الخيطية =2 ...والعبارة البرمجية ++ تزيد بمقدار 1 لكلاهما فى ذات الوقت (هل تذكر اول المقالة التسارع لإنجاز نفس المهمة ..أرجو ذلك لأنى زودتها حبتين :) )وبالتالى كانت المحصلة النهائية هى زيادة واحدة بمقدار واحد وهو مشكلتنا المسماه بالـRace Condition وهى ما تسببت فى ظهور قيم متغيرة كل مرة على حسب سرعة المعالجة او اى عوامل خارجية من نظام التشغيل الى العملية المشتركة معك فى نفس المعالجة ............الخ .
طبعا هذه المشكلة على امل ان تحل . وإلا فما الجدوى من المعالجة متعددة الخيوط اذا كانت النتائج على غير المتوقع ؟؟!!
.وعلى حسب ما اطلعت عليه قريبا ان الاصدار .NET 4.0 أصلا موجه للمعالجة المتعددة الخيوط او المتزامنة عموما ..وهناك لغة تسمى Parallel C# تحت التطوير تبحث فى هذا المجال ....
خلاصة القول .ما اردته والله حسيبى ان افيدكم باخر ما اطلعت عليه وفهمته من باب نشر العلم ..ادعوا لى بالخير والفهم ..وارجوا ان كنت اخطأت فابالله ذكرونى وصوبوا لى ..فانا لازلت طويلب علم ....وفقنى الله واياكم والمسلمين اجمعين.
فى امان الله[/
