• 0
DEVELOPER.MORKANE

ما فائدة المتقلبة اي "volatile"

سؤال

السلام عليكم ورحمة الله تعالى وبركاته 

اخواني اريد منكم امثلة على الكلمة المفتحية "volatile" ولماذا تستعمل ومتى ؟؟.

https://msdn.microsoft.com/en-us/library/x13ttww7.aspx

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

3 إجابة على هذا السؤال .

  • 0

ماذا تعني الكلمة "volatile" في #C ؟

الكلمة الأساسية "volatile" تشير إلى أن "الحقل" يمكن أن يطرأ
عليه تعديلات من قبل "threads" متعددة في نفس الوقت، الحقول
التي تم تعريفها "volatile" لا تخضع لتحسينات المترجم (compiler)
التي تفترض وصول "thread" واحدة للحقل، هذا يضمن أن آخر قيمة
محدثة موجودة في "الحقل" في جميع الأوقات.


تستعمل كلمة "volatile" عادة على "الحقول" التي سيتم الوصول
إليها بـ "threads" متعددة، الكلمة الأساسية "volatile" يمكن
تطبيقها على "الحقول" من هذه الأنواع :


 

  • المراجع (Reference types)
  • المؤشرات (Pointer types) { في سياق 'غير آمن' (unsafe context) }
  • الأنواع مثل (sbyte, byte, short, ushort, int, uint, char, float, bool)
  • enum بهذه الأنواع الأساسية (byte, sbyte, short, ushort, int, uint)
  • كل الأنواع العامة المعروفة بـ 'المراجع' (Reference types)
  • بما في ذلك أنواع 'المراجع' (IntPtr, UIntPtr)

الكلمة "volatile" يمكن أن تطبق فقط على حقول الـ "class" أو الـ "struct".
الحقول المحلية "locals" لا يمكن أن تعرف على أنها "volatile".


لماذا نستعمل "volatile" :

المترجم (the compiler) يقوم بالعديد من التحسينات في الخلف
بعض هذه التحسينات قد تعيد ترتيب القيم المتراكمة للـ "حقل"

أي نظريا في حال كانت هناك "threads" متعددة تصل لقيمة
الحقل في نفس الوقت يمكن لأحد منهم أن تقرأ قيمة الحقل
التي في الحقيقة قد تغيرت قبل بضع أجزاء من الثانية، أي أن
أحدى الـ "threads" ستقرأ قيمة "كانت" موجودة عوض القيمة

"الحالية" للحقل، "volatile" ستضمن عدم حدوث هذا "التحسين"
الذي قد يكون سئ للغاية حيث ستخبر الكومبايلر بأن لا يجرى
هذا "التحسين" على "الحقل" المقصود. 


ما هي الكلمة المعادلة لـ "volatile" في VB.NET ؟

لا يوجد كلمة أساسية معادلة لـ "volatile" في الـ vb.net
في الـ #C كلمة "volatile" فقط تخبر الكومبايلر بأن يتعامل
مع الحقل بشكل مختلف في الـ vb.net يمكن القيام بهذا 'يدويا'.
المثال التالي لا يجسد خطأ يمكن أن يحدث إذا إستغنينا عن "volatile"
بل فقط يجسد كيفية تنفيذ آلية "volatile" في الـ vb.net يدويا،
السبب وراء عدم إنتاج كود يظهر مخاطر عدم إستعمال "volatile"
هو أنه من النادر جدا أن نجعل 'عن قصد' عدد مختلف من الـ "threads"
تصل لحقل ما في نفس الوقت و لكن هذا يكاد يحدث في كل وقت
عندما نستعمل الـ "multithreading" و هذا يمكن أن يكون خطيرا جدا.

منقول 

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

يعطيك العافي اخي على الشرح شرحك ممتاز بس لو تعطيني مثال بكون احسن وبرك الله فيك.

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

volatile تستخدم لمنع مترجم الـC# أو مترجم الـJIT من إجراء أي تحسين optimization قد يؤدي لعدم القراءة أو الكتابة للمتغير مباشرة في الذاكرة، مثلاً هنا:

using System;
using System.Threading;

namespace ConsoleApplication
{
    internal class Program
    {
        private static void Main()
        {
            var test = new Test();

            new Thread(() =>
            {
                Thread.Sleep(1000);
                test.Continue = false;
            }).Start();

            Console.WriteLine("Entering loop");
            while (true)
            {
                if (test.Continue == false)
                    break;
            }
            Console.WriteLine("Leaving loop");
        }

        internal class Test
        {
            public bool Continue = true;
        }
    }
}

هناك خيط معالجة فرعي ينتظر لثانية ثم يغير قيمة Continue إلى false، خيط المعالجة الرئيسي يستمر في حلقة تكرار طالما أن Continue = true ثم يخرج من حلقة التكرار إذا تغيرت قيمة Continue إلى false.

هذا المفترض، إلا أنه عند بناء البرنامج في وضع الـRelease (تفعل التحسينات)، قد يقوم مترجم الـJIT خطأ بتحسين حلقة التكرار بحيث يحذف جملة if إلى مايكافئ:

while (true)
{
}

بحيث تستمر حلقة التكرار للأبد، أي أن البرنامج لم يعد يقرأ القيمة من الذاكرة بل أصبح إما يقرأ من مكان آخر كمسجل المعالج أو لم يعد يقرأ القيمة أصلاً، عندي حسن الكود للتالي:

02B504B8  movzx       eax,byte ptr [eax+4]  
            while (true)
            {
                if (test.Continue == false)
02B504BC  test        eax,eax                   #### ليس لها أي فائدة عند العودة لها مرة أخرى
            while (true)
            {
                if (test.Continue == false)
02B504BE  jne         02B504BC                  #### eax قفزة للتعليمة السابقة دون أي تعليمة قد تغير قيمة المسجل
                    break;
            }
            Console.WriteLine("Leaving loop");
02B504C0  mov         ecx,dword ptr ds:[3DB22A8h] 

أي أنه عملياً حذف جملة if ولم يعد يقرأ القيمة Continue، هذا التحسين معقول لو أن هناك فقط خيط معالجة واحد، فلا يوجد شيء قد يغير قيمة Continue في خيط المعالجة الرئيسي، والمترجم يقوم بالتحسين وفق هذه الفرضية فلايوجد شيء عند تحليله للبرنامج يشير لعكس هذا.

لتفادي مثل هذا النوع من التحسينات نحتاج لاستخدام volatile مع Continue هكذا:

...
            public volatile bool Continue = true;
...

لمنع المترجم من عمل أي تحسين يخص هذا المتغير قد يؤدي لتلك النتيجة غير المرغوبة.

انتبه أن volatile نادر جداً أن تستخدم، فأي بيانات يتم الوصول لها من أكثر من خيط معالجة عرضة لحالة تسمى التسابق race condition، وجود volatile لن يحميك منها فهي ليست وسيلة مزامنة، هنا مثلاً لدينا خيطان معالجة يقوم كلاً منهما بزيادة Value بواحد:

using System;
using System.Threading;

namespace ConsoleApplication
{
    internal class Program
    {
        private static void Main()
        {
            var test = new Test();

            var thread = new Thread(() =>
            {
                for (var i = 0; i < 1000000; ++i)
                {
                    test.Value += 1;
                }
            });

            thread.Start();

            for (var i = 0; i < 1000000; ++i)
            {
                test.Value += 1;
            }

            thread.Join();

            Console.WriteLine(test.Value); // 2000000 ?!
        }

        internal class Test
        {
            public volatile int Value;
        }
    }
}

النتيجة المتوقعة أن تكون قيمة Value = 2000000، إلا أنك غالباً ستجد قيم عشوائية مختلفة بسبب حالة التسابق، وجود volatile لن يغير شيء، تحتاج دوماً لمزامنة الوصول للمتغير المشترك الذي تقرأه وتكتب له أكثر من خيط معالجة سواء باستخدام مثلاً قفل lock على المتغير test، أو استخدام دوال عائلة Interlocked لمثل هذه الحالة البسيطة:

using System;
using System.Threading;

namespace ConsoleApplication
{
    internal class Program
    {
        private static void Main()
        {
            var test = new Test();

            var thread = new Thread(() =>
            {
                for (var i = 0; i < 1000000; ++i)
                {
                    Interlocked.Increment(ref test.Value); // atomic test.Value += 1
                }
            });

            thread.Start();

            for (var i = 0; i < 1000000; ++i)
            {
                Interlocked.Increment(ref test.Value); // atomic test.Value += 1
            }

            thread.Join();

            Console.WriteLine(test.Value); // = 2000000
        }

        internal class Test
        {
            public int Value;
        }
    }
}

عند استخدام أدوات المزامنة تلك فلا حاجة إلى volatile، لأنها تضمن أن المتغير يقرأ ويكتب مباشرة في الذاكرة.

المثال الأول باستخدام القفل:

using System;
using System.Threading;

namespace ConsoleApplication
{
    internal class Program
    {
        private static void Main()
        {
            var test = new Test();

            new Thread(() =>
            {
                Thread.Sleep(1000);
                lock (test)
                {
                    test.Continue = false;
                }
            }).Start();

            Console.WriteLine("Entering loop");
            while (true)
            {
                lock (test)
                {
                    if (test.Continue == false)
                        break;
                }
            }
            Console.WriteLine("Leaving loop");
        }

        internal class Test
        {
            public bool Continue = true;
        }
    }
}

 

تم تعديل بواسطه Mr.B
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

من فضلك سجل دخول لتتمكن من التعليق

ستتمكن من اضافه تعليقات بعد التسجيل



سجل دخولك الان

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .