متقلب (برمجة الحاسوب)

في برمجة الحاسوب ، يُقال إن المتغير متقلب إذا كان من الممكن قراءة قيمته أو تعديلها بشكل غير متزامن بواسطة أي شيء آخر غير سلسلة التنفيذ الحالية . قد تتغير قيمة المتغير volatileتلقائيًا لأسباب مثل: مشاركة القيم مع سلاسل تنفيذ أخرى؛ مشاركة القيم مع معالجات الإشارات غير المتزامنة ؛ الوصول إلى أجهزة الحاسوب عبر الإدخال/الإخراج المُمَثَّل في الذاكرة (حيث يمكن استقبال الرسائل من الأجهزة الطرفية وإرسالها عن طريق القراءة من الذاكرة والكتابة إليها). يختلف دعم حالات الاستخدام هذه اختلافًا كبيرًا بين لغات البرمجة التي تحتوي على volatileالكلمة المفتاحية volatile. قد يكون للتقلب آثار على اصطلاحات استدعاء الدوال وكيفية تخزين المتغيرات والوصول إليها وتخزينها مؤقتًا.

في لغتي C و C++

في لغتي C و C++، volatileهو مؤهل نوع ، مثل const، وهو جزء من نوع (على سبيل المثال نوع متغير أو حقل).

volatileيُشرح سلوك الكلمة المفتاحية في لغتي C و C++ أحيانًا من منظور كبح تحسينات المُصرّف المُحسِّن : 1- عدم حذف volatileعمليات القراءة والكتابة الموجودة، 2- عدم إضافة volatileعمليات قراءة وكتابة جديدة، 3- عدم إعادة ترتيب volatileعمليات القراءة والكتابة. مع ذلك، يُعد هذا التعريف تقريبيًا فقط لتسهيل الأمر على المتعلمين الجدد، ولا ينبغي الاعتماد عليه لكتابة برامج إنتاجية حقيقية.

في لغة C، وبالتالي في لغة C++، volatileكان المقصود من الكلمة المفتاحية ما يلي: [ 1 ]

تسمح معايير لغتي C و C++ بكتابة شيفرة قابلة للنقل تتشارك القيم بين longjmpالكائنات volatile، كما تسمح بكتابة شيفرة قابلة للنقل تتشارك القيم بين معالجات الإشارات وبقية الشيفرة في volatilesig_atomic_tالكائنات. أي استخدام آخر volatileللكلمة المفتاحية في لغتي C و C++ غير قابل للنقل أو غير صحيح بطبيعته. على وجه الخصوص، كتابة شيفرة باستخدام volatileالكلمة المفتاحية لأجهزة الإدخال/الإخراج المُرتبطة بالذاكرة غير قابلة للنقل بطبيعتها، وتتطلب دائمًا معرفة متعمقة بتنفيذ C/C++ والمنصة المستهدفة.

تعدد الخيوط

من المفاهيم الخاطئة الشائعة أن volatileالكلمة المفتاحية مفيدة في كتابة أكواد متعددة الخيوط قابلة للنقل في لغتي C وC++. في الواقع، لمvolatile تُستخدم هذه الكلمة المفتاحية في لغتي C وC++ كأداة مفيدة وقابلة للنقل في أي سيناريو متعدد الخيوط. [ 2 ] [ 3 ] [ 4 ] [ 5 ] على عكس لغتي البرمجة Java و C# ، فإن العمليات على المتغيرات في C وC++ ليست ذرية ، ولا توفر ضمانات كافية لترتيب الذاكرة (أي حواجز الذاكرة ). ببساطة، لا توفر معظم مُجمِّعات C وC++، وروابطها، وبيئات تشغيلها ضمانات ترتيب الذاكرة اللازمة لجعل الكلمة المفتاحية مفيدة في أي سيناريو متعدد الخيوط. قبل معايير C11 و C++11 ، كان المبرمجون مُضطرين للاعتماد على الضمانات من التطبيقات والمنصات الفردية (مثل POSIX وWIN32) لكتابة أكواد متعددة الخيوط . مع معايير C11 وC++11 الحديثة، يُمكن للمبرمجين كتابة أكواد متعددة الخيوط قابلة للنقل باستخدام بنيات قابلة للنقل جديدة مثل القوالب. [ 6 ]volatilevolatilevolatilestd::atomic<T>

مثال على الإدخال/الإخراج المُمَثَّل في الذاكرة بلغة C

في هذا المثال، يقوم الكود بتعيين القيمة المخزنة في المتغير fooإلى 0. ثم يبدأ في استطلاع تلك القيمة بشكل متكرر حتى تتغير إلى 255:

static int foo ;دالة فارغة bar ( فارغة ) { foo = 0 ;بينما ( foo != 255 ) {} }

سيلاحظ المترجم المُحسِّن أنه لا يمكن لأي كود آخر تغيير القيمة المخزنة في المتغير foo، وسيفترض أنها ستظل مساوية للقيمة الأصلية 0دائمًا. لذلك، سيستبدل المترجم جسم الدالة بحلقة تكرار لا نهائية مشابهة لما يلي:

void bar_optimized ( void ) { foo = 0 ;بينما ( صحيح ) {} }

مع ذلك، قد يُشير المبرمج fooإلى عنصر آخر في نظام الحاسوب، مثل سجلّات الأجهزة المتصلة بوحدة المعالجة المركزية ، والتي قد تُغيّر قيمة المتغير fooأثناء تشغيل هذا الكود. (لا يتضمن هذا المثال تفاصيل كيفية الإشارة fooإلى سجلّات الأجهزة المتصلة بوحدة المعالجة المركزية). بدون volatileالكلمة المفتاحية، من المرجح أن يُحوّل المُصرّف المُحسِّن الكود من المثال الأول الذي يتضمن قراءة البيانات داخل الحلقة إلى المثال الثاني الذي لا يتضمنها، وذلك كجزء من تحسين نقل الكود غير المتأثر بالحلقات ، وبالتالي لن يلاحظ الكود على الأرجح التغيير الذي ينتظره.

لمنع المُصرّف من إجراء هذا التحسين، volatileيمكن استخدام الكلمة المفتاحية التالية:

ثابت متقلب عدد صحيح foo ؛دالة فارغة bar ( فارغة ) { foo = 0 ;بينما ( foo != 255 ) {} }

تمنع الكلمة volatileالمفتاحية المترجم من نقل القراءة خارج الحلقة، وبالتالي سيلاحظ الكود التغيير المتوقع في المتغير foo.

مقارنة التحسين في لغة C

توضح برامج لغة C التالية، ومقتطفات لغة التجميع المصاحبة لها، كيف volatileتؤثر الكلمة المفتاحية على مخرجات المُصرّف. وكان المُصرّف في هذه الحالة هو GCC .

عند معاينة كود التجميع، يتضح جليًا أن الكود المُولّد باستخدام volatileالكائنات أكثر تفصيلًا، مما يجعله أطول volatileليُلبي متطلبات طبيعة الكائنات. volatileتمنع الكلمة المفتاحية المُصرّف من إجراء تحسينات على الكود الذي يتضمن كائنات متقلبة، وبالتالي تضمن أن كل عملية إسناد وقراءة لمتغير متقلب لها وصول مُقابل إلى الذاكرة. بدون هذه volatileالكلمة المفتاحية، يعلم المُصرّف أن المتغير لا يحتاج إلى إعادة قراءة من الذاكرة في كل استخدام، لأنه لا ينبغي أن تكون هناك أي عمليات كتابة إلى موقعه في الذاكرة من أي خيط أو عملية أخرى.

عيوب في المُترجم

على عكس ميزات لغات C و C++ الأخرى، volatileلا تحظى الكلمة المفتاحية بدعم كافٍ من معظم تطبيقات C/C++، حتى للاستخدامات القابلة للنقل وفقًا لمعايير C و C++. وتعاني معظم تطبيقات C/C++ من أخطاء برمجية فيما يتعلق بسلوك هذه الكلمة volatileالمفتاحية. [ 7 ] [ 8 ] لذا، ينبغي على المبرمجين توخي الحذر الشديد عند استخدامها volatileفي C و C++.

في لغة جافا

في جميع الإصدارات الحديثة من لغة برمجة جافا ، volatileتوفر الكلمة المفتاحية الضمانات التالية:

  • volatileعمليات القراءة والكتابة ذرية . على وجه الخصوص، لن يحدث أي تمزق في البيانات عند قراءة أو كتابة longالحقول . ( ينطبق ضمان الذرية فقط على القيمة الأولية أو القيمة المرجعية، وليس على أي قيمة كائن).doublevolatilevolatile
  • يوجد ترتيب عالمي موحد لجميع volatileعمليات القراءة والكتابة. بمعنى آخر، volatileتقرأ عملية القراءة القيمة الحالية (وليس قيمة سابقة أو مستقبلية)، volatileوتتفق جميع عمليات القراءة على ترتيب عالمي موحد لعمليات volatileالكتابة.
  • volatileتتضمن عمليات القراءة والكتابة دلالات "الاستحواذ" و"التحرير" لحواجز الذاكرة (المعروفة في معيار جافا باسم " يحدث قبل "). [ 9 ] [ 10 ] بعبارة أخرى، volatileتوفر هذه الخاصية ضمانات بشأن الترتيب النسبي لعمليات volatileالقراءة volatileوالكتابة. بعبارة أخرى، volatileتوفر هذه الخاصية بشكل أساسي نفس ضمانات رؤية الذاكرة التي توفرها كتلة متزامنة في جافا (ولكن بدون ضمانات الاستبعاد المتبادل التي توفرها الكتلة المتزامنة ).

تُشكّل هذه الضمانات مجتمعةً volatileبنيةً مفيدةً للمعالجة المتعددة في جافا . وعلى وجه الخصوص، تعمل خوارزمية القفل المزدوجة التحقق النموذجية volatileبشكل صحيح في جافا . [ 11 ]

الإصدارات المبكرة من جافا

قبل إصدار جافا 5، لم يضمن معيار جافا الترتيب النسبي لعمليات القراءة والكتابة volatileغير المرتبطة volatileبالذاكرة. بعبارة أخرى، volatileلم يكن لـ Java دلالات "الاستحواذ" و"تحرير" حواجز الذاكرة . وقد حدّ هذا بشكل كبير من استخدامها كبنية للمعالجة المتعددة الخيوط . وعلى وجه الخصوص، لم تكن خوارزمية القفل المزدوجة التحقق النموذجية تعمل بشكل صحيح.volatile

شركة#

في لغة C# ، volatileيضمن هذا أن الكود الذي يصل إلى الحقل لا يخضع لبعض التحسينات غير الآمنة للخيوط والتي قد يُجريها المُصرّف أو بيئة CLR أو الأجهزة. عند وضع علامة على حقل volatile، يُوجّه المُصرّف لإنشاء "حاجز ذاكرة" أو "سياج" حوله، مما يمنع إعادة ترتيب التعليمات أو التخزين المؤقت المرتبط بالحقل. عند قراءة volatileحقل، يُنشئ المُصرّف سياجًا للاستحواذ ، مما يمنع عمليات القراءة والكتابة الأخرى للحقل من الانتقال قبل هذا السياج. عند الكتابة إلى volatileحقل، يُنشئ المُصرّف سياجًا للإفراج ؛ يمنع هذا السياج عمليات القراءة والكتابة الأخرى للحقل من الانتقال بعد هذا السياج. [ 12 ]

لا يمكن وضع علامة إلا على الأنواع التالية : جميع volatileأنواع المراجع، Singleوجميع الأنواع المُعدّدة التي يكون نوعها الأساسي من النوع ، أو . [ 13 ] ( يستثنى من ذلك Booleanهياكل القيم ، بالإضافة إلى الأنواع الأولية . )ByteSByteInt16UInt16Int32UInt32CharByteSByteInt16UInt16Int32UInt32DoubleInt64UInt64Decimal

volatileلا يدعم استخدام الكلمة المفتاحية الحقول التي يتم تمريرها بالمرجع أو المتغيرات المحلية الملتقطة ؛ في هذه الحالات، Thread.VolatileReadيجب Thread.VolatileWriteاستخدام بدلاً من ذلك. [ 12 ]

في الواقع، تُعطّل هذه الطرق بعض التحسينات التي يُجريها عادةً مُصرّف لغة C#، أو مُصرّف JIT، أو وحدة المعالجة المركزية نفسها. وتُعدّ الضمانات التي تُوفّرها هذه الطرق Thread.VolatileReadمجموعةً Thread.VolatileWriteشاملةً للضمانات التي تُوفّرها الكلمة volatileالمفتاحية: فبدلاً من إنشاء "حاجز جزئي" (أي حاجز الاستحواذ الذي يمنع فقط إعادة ترتيب التعليمات وتخزينها مؤقتًا قبلها)، VolatileReadتُنشئ VolatileWriteهذه الطرق "حاجزًا كاملاً" يمنع إعادة ترتيب التعليمات وتخزين ذلك الحقل مؤقتًا في كلا الاتجاهين. [ 12 ] وتعمل هذه الطرق على النحو التالي: [ 14 ]

  • تُجبر هذه Thread.VolatileWriteالطريقة على كتابة القيمة الموجودة في الحقل عند نقطة الاستدعاء. بالإضافة إلى ذلك، يجب أن تتم أي عمليات تحميل وتخزين سابقة بترتيب البرنامج قبل الاستدعاء، VolatileWriteوأي عمليات تحميل وتخزين لاحقة بترتيب البرنامج يجب أن تتم بعد الاستدعاء.
  • تُجبر هذه Thread.VolatileReadالطريقة على قراءة القيمة الموجودة في الحقل عند نقطة الاستدعاء. بالإضافة إلى ذلك، يجب أن تتم أي عمليات تحميل وتخزين سابقة بترتيب البرنامج قبل الاستدعاء، VolatileReadوأي عمليات تحميل وتخزين لاحقة بترتيب البرنامج يجب أن تتم بعد الاستدعاء.

تُنشئ الطريقتان Thread.VolatileRead`and` Thread.VolatileWriteحاجزًا كاملًا عن طريق استدعاء Thread.MemoryBarrierالطريقة `and`، التي تُنشئ حاجزًا للذاكرة يعمل في كلا الاتجاهين. بالإضافة إلى دوافع استخدام الحاجز الكامل المذكورة أعلاه، تتمثل إحدى المشكلات المحتملة مع الكلمة volatileالمفتاحية `and`، والتي يتم حلها باستخدام حاجز كامل مُنشأ بواسطة `and`، Thread.MemoryBarrierفيما يلي: نظرًا للطبيعة غير المتماثلة للحواجز النصفية، volatileقد يُبدّل المُصرّف ترتيب التنفيذ لحقل يحتوي على تعليمة كتابة متبوعة بتعليمات قراءة. ولأن الحواجز الكاملة متماثلة، فإن هذه المشكلة لا تُشكّل عائقًا عند استخدام `and` Thread.MemoryBarrier. [ 12 ]

في لغة فورتران

VOLATILEيُعدّ جزءًا من معيار Fortran 2003 ، [ 15 ] على الرغم من أن الإصدارات السابقة كانت تدعمه كإضافة. volatileكما أن تعريف جميع المتغيرات في دالة ما يُفيد في اكتشاف الأخطاء المتعلقة بالأسماء المستعارة .

عدد صحيح ، متقلب :: i ! عندما لا يتم تعريف متقلب، يكون السطران التاليان من التعليمات البرمجية متطابقين: write ( * , * ) i ** 2 ! يقوم بتحميل المتغير i مرة واحدة من الذاكرة ويضرب تلك القيمة في نفسها. write ( * , * ) i * i ! يقوم بتحميل المتغير i مرتين من الذاكرة ويضرب تلك القيم.

من خلال الرجوع الدائم إلى ذاكرة المتغيرات المتغيرة (VOLATILE)، يُمنع مُترجم لغة فورتران من إعادة ترتيب عمليات القراءة أو الكتابة إلى هذه المتغيرات. وهذا يجعل الإجراءات التي تتم في هذا الخيط مرئية للخيوط الأخرى، والعكس صحيح. [ 16 ]

يؤدي استخدام VOLATILE إلى تقليل التحسين، بل وقد يمنعه تمامًا. [ 17 ]

مراجع

  1. "منشور حول لجنة معايير لغة C++" .
  2. "كلمة مفتاحية متطايرة في Visual C++" . مايكروسوفت MSDN . 21 سبتمبر 2021.
  3. "وثائق نواة لينكس - لماذا لا ينبغي استخدام فئة النوع "volatile"" . kernel.org .
  4. سكوت مايرز؛ أندريه ألكسندرسكو (2004). "لغة سي++ ومخاطر القفل المزدوج" (ملف PDF) . DDJ .
  5. جيريمي أندروز (2007). "لينكس: خرافة متقلبة" . kerneltrap.org. مؤرشف من الأصل بتاريخ 20 يونيو 2010. تم الاطلاع عليه بتاريخ 9 يناير 2011 .
  6. "volatile (C++)" . مايكروسوفت MSDN . 21 سبتمبر 2021.
  7. إيدي، إريك؛ ريغير، جون (أكتوبر 2008). "المعاملات المتقلبة تُترجم بشكل خاطئ، وكيفية التعامل مع ذلك" (ملف PDF) . وقائع المؤتمر الدولي الثامن لجمعية ACM وIEEE حول البرمجيات المدمجة (EMSOFT)، أتلانتا، جورجيا، الولايات المتحدة الأمريكية - عبر cs.utah.edu.
  8. "الأخطاء البرمجية المتقلبة، بعد ثلاث سنوات - متأصلة في الأوساط الأكاديمية" . blog.regehr.org . تم الاطلاع عليه بتاريخ 28-08-2024 .
  9. القسم 17.4.4: ترتيب التزامن "مواصفات لغة جافا®، إصدار جافا SE 7" . شركة أوراكل . 2013. تم الاطلاع عليه بتاريخ 12 مايو 2013 .
  10. "التزامن في جافا: فهم الكلمة المفتاحية 'volatile'" . dzone.com. 2021-03-08. مؤرشف من الأصل في 2021-05-09 . تم الاطلاع عليه في 2021-05-09 .
  11. نيل كوفي. "القفل المزدوج التحقق (DCL) وكيفية إصلاحه" . جافامكس . تم الاسترجاع في 19-09-2009 .
  12. 1 2 3 4 ألباهاري، جوزيف. "الجزء 4: المعالجة المتوازية المتقدمة" . المعالجة المتوازية في لغة سي شارب . دار نشر أورايلي ميديا. مؤرشف من الأصل في 12 ديسمبر 2019. تم الاطلاع عليه في 9 ديسمبر 2019 .{{cite web}}: CS1 maint: bot: حالة عنوان URL الأصلي غير معروفة ( رابط )
  13. ↑ ريختر، جيفري ( 11 فبراير 2010). "الفصل 7: الثوابت والحقول". CLR عبر C# . مطبعة مايكروسوفت. ص 183. ISBN  978-0-7356-2704-8.
  14. ريختر، جيفري (11 فبراير 2010). "الفصل 28: بنيات مزامنة الخيوط الأولية". CLR عبر C# . مطبعة مايكروسوفت. الصفحات 797-803 . ISBN  978-0-7356-2704-8.
  15. "الخاصية والبيان المتقلبان" . كراي. مؤرشف من الأصل بتاريخ 23 يناير 2018. تم الاطلاع عليه بتاريخ 22 أبريل 2016 .
  16. "المصفوفات المتطايرة والمشتركة في لغة فورتران" . Intel.com .
  17. "متقلب" . Oracle.com .