التخزين المحلي للخيط

في برمجة الحاسوب ، يُعدّ التخزين المحلي للخيوط ( TLS ) أسلوبًا لإدارة الذاكرة يستخدم ذاكرة ثابتة أو عامة خاصة بخيط معين . يسمح هذا المفهوم بتخزين البيانات التي تبدو عامة في نظام ذي خيوط منفصلة.

تفرض العديد من الأنظمة قيودًا على حجم كتلة الذاكرة الخاصة بالخيط، وغالبًا ما تكون هذه القيود صارمة للغاية. من ناحية أخرى، إذا كان النظام قادرًا على توفير متغير محلي للخيط بحجم عنوان ذاكرة (مؤشر)، فإن ذلك يسمح باستخدام كتل ذاكرة ذات أحجام عشوائية بطريقة محلية للخيط، وذلك بتخصيص كتلة الذاكرة هذه ديناميكيًا وتخزين عنوانها في المتغير المحلي للخيط. في أجهزة RISC ، غالبًا ما يحجز اصطلاح الاستدعاء سجل مؤشر الخيط لهذا الغرض.

الاستخدام

على الرغم من أن استخدام المتغيرات العامة غير مستحب عمومًا في البرمجة الحديثة، إلا أن بعض أنظمة التشغيل القديمة، مثل يونكس، صُممت في الأصل لأجهزة أحادية المعالج ، وغالبًا ما تستخدم المتغيرات العامة لتخزين القيم المهمة. ومن الأمثلة على ذلك استخدام errnoالعديد من دوال مكتبة C للمتغير `var`. في الأجهزة الحديثة، حيث قد تُعدّل عدة سلاسل errnoعمليات المتغير، قد يؤدي استدعاء دالة نظام في إحدى سلاسل العمليات إلى استبدال القيمة التي تم تعيينها مسبقًا بواسطة استدعاء دالة نظام أخرى في سلسلة عمليات مختلفة، وربما قبل أن يتمكن الكود التالي في تلك السلسلة من التحقق من حالة الخطأ. الحل هو استخدام errnoمتغير يبدو وكأنه عام، ولكنه يُخزّن فعليًا في مخزن ذاكرة خاص بكل سلسلة عمليات، وهو ما يُعرف بالتخزين المحلي لسلسلة العمليات.

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

تطبيق ويندوز

يمكن استخدام وظيفة واجهة برمجة التطبيقات ( API) للحصول على فهرس فتحة TLS غير مستخدم ؛ وسيتم اعتبار فهرس فتحة TLS "مستخدمًا" بعد ذلك.TlsAlloc

تُستخدم الدالتان `and` لقراءة وكتابة عنوان ذاكرة إلى متغير محلي خاص بالخيط، مُحدد بواسطة فهرس خانة TlsGetValueTLS . تؤثر الدالة `and` فقط على المتغير الخاص بالخيط الحالي. ويمكن استدعاء الدالة `resource` لتحرير فهرس خانة TLS .TlsSetValueTlsSetValueTlsFree

يوجد قسم معلومات مؤشر الترابط Win32 لكل مؤشر ترابط. أحد عناصر هذا القسم هو جدول التخزين المحلي لمؤشر الترابط. [ 1 ] كل استدعاء للدالة TlsAllocيُعيد فهرسًا فريدًا في هذا الجدول. يمكن لكل مؤشر ترابط استخدام TlsSetValue(index, value)القيمة المحددة والحصول عليها بشكل مستقل عبر الدالة TlsGetValue(index)، لأن هذه الدوال تُعيّن وتبحث عن عنصر في جدول مؤشر الترابط الخاص به.

إلى جانب عائلة دوال TlsXxx، يمكن لملفات ويندوز التنفيذية تعريف قسم يُربط بصفحة مختلفة لكل خيط من خيوط العملية قيد التنفيذ. على عكس قيم TlsXxx، يمكن أن تحتوي هذه الصفحات على عناوين عشوائية وصالحة. مع ذلك، تختلف هذه العناوين لكل خيط تنفيذ، وبالتالي لا ينبغي تمريرها إلى الدوال غير المتزامنة (التي قد تُنفذ في خيط مختلف) أو إلى التعليمات البرمجية التي تفترض أن العنوان الظاهري فريد ضمن العملية بأكملها. تُدار أقسام TLS باستخدام ترحيل الذاكرة ، ويُحدد حجمها بحجم صفحة (4 كيلوبايت على أجهزة x86). لا يمكن تعريف هذه الأقسام إلا داخل الملف التنفيذي الرئيسي للبرنامج - لا ينبغي أن تحتوي مكتبات DLL على مثل هذه الأقسام، لأنها لا تُهيأ بشكل صحيح عند تحميلها باستخدام LoadLibrary.

تطبيق Pthreads

في واجهة برمجة تطبيقات Pthreads ، يتم تحديد الذاكرة المحلية للخيط باستخدام مصطلح البيانات الخاصة بالخيط.

تُستخدم الدالتان pthread_key_createو pthread_key_deleteعلى التوالي لإنشاء وحذف مفتاح لبيانات خاصة بكل عملية. يُترك نوع المفتاح غير مُحدد ويُشار إليه بـ pthread_key_t. يمكن لجميع العمليات رؤية هذا المفتاح. في كل عملية، يمكن ربط المفتاح ببيانات خاصة بتلك العملية عبر pthread_setspecific. يمكن استرجاع البيانات لاحقًا باستخدام pthread_getspecific.

بالإضافة إلى ذلك، pthread_key_createيمكن للبرنامج اختياريًا قبول دالة تدمير تُستدعى تلقائيًا عند انتهاء الخيط، إذا لم تكن البيانات الخاصة بالخيط فارغة (NULL ). تستقبل دالة التدمير القيمة المرتبطة بالمفتاح كمعامل لتنفيذ عمليات التنظيف (إغلاق الاتصالات، تحرير الذاكرة، إلخ). حتى عند تحديد دالة تدمير، يجب على البرنامج استدعاء دالة pthread_key_deleteلتحرير البيانات الخاصة بالخيط على مستوى العملية (حيث تُحرر دالة التدمير البيانات المحلية للخيط فقط).

التنفيذ الخاص باللغة

إلى جانب الاعتماد على المبرمجين لاستدعاء وظائف واجهة برمجة التطبيقات المناسبة، من الممكن أيضًا توسيع لغة البرمجة لدعم التخزين المحلي للخيوط (TLS).

لغة C ولغة C++

في لغة C11_Thread_local ، تُستخدم الكلمة المفتاحية لتعريف المتغيرات المحلية الخاصة بالخيط. <threads.h>ويُعرّف ملف الرأس، إن كان مدعومًا، thread_localمرادفًا لتلك الكلمة المفتاحية.

في لغة C11، <threads.h>تُعرّف أيضًا عددًا من الدوال لاسترجاع وتغيير وتدمير مساحة تخزين محلية خاصة بالخيط، باستخدام أسماء تبدأ بـ tss_. في لغة C23، thread_localتصبح هي نفسها كلمة مفتاحية. [ 2 ]

thread_local int foo = 0 ;

يُقدّم الإصدار C++11 الكلمة المفتاحية thread_local[ 3 ] التي يمكن استخدامها في الحالات التالية

  • المتغيرات على مستوى مساحة الاسم (المتغيرات العامة)
  • متغيرات الملف الثابتة
  • المتغيرات الثابتة للدالة
  • المتغيرات العضوية الثابتة

إلى جانب ذلك، توفر تطبيقات المترجمات المختلفة طرقًا محددة لتعريف المتغيرات المحلية للخيوط:

في إصدارات ويندوز السابقة لإصداري Vista وServer 2008، __declspec(thread)يعمل هذا البرنامج مع ملفات DLL فقط عندما تكون هذه الملفات مرتبطة بالملف التنفيذي، ولن يعمل مع الملفات التي يتم تحميلها باستخدام LoadLibrary() (قد يحدث خطأ في الحماية أو تلف في البيانات). [ 10 ]

لغة Common Lisp وغيرها من اللهجات

توفر لغة Common Lisp ميزة تسمى المتغيرات ذات النطاق الديناميكي .

للمتغيرات الديناميكية ربط خاص باستدعاء دالة وجميع العناصر الفرعية التي تستدعيها تلك الدالة.

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

على سبيل المثال، يحدد المتغير القياسي *print-base*نظام العد الافتراضي الذي تُطبع به الأعداد الصحيحة. إذا تم تجاوز هذا المتغير، فسيطبع البرنامج الأعداد الصحيحة بنظام عد بديل حتى ينتهي التجاوز.

ستطبع الدالة foo وأبناؤها بالصيغة الست عشرية: ( let (( *print-base* 16 )) ( foo ))

إذا كان بإمكان الدوال التنفيذ بشكل متزامن على خيوط مختلفة، فيجب أن يكون هذا الربط محليًا للخيط بشكل صحيح، وإلا فإن كل خيط سيتنازع على من يتحكم في أساس الطباعة العالمي.

د

في الإصدار الثاني من لغة D ، تكون جميع المتغيرات الثابتة والعامة محلية للخيط افتراضيًا، ويتم تعريفها بصيغة مشابهة للمتغيرات العامة والثابتة "العادية" في اللغات الأخرى. يجب طلب المتغيرات العامة صراحةً باستخدام الكلمة المفتاحية shared .

int threadLocal ; // هذا متغير خاص بالخيط. shared int global ; // هذا متغير عام مشترك بين جميع الخيوط.

تُستخدم الكلمة المفتاحية `shared` كفئة تخزين، وكأداة لتحديد النوع - تخضع المتغيرات المشتركة لبعض القيود التي تضمن سلامة البيانات بشكل ثابت. [ 13 ] لتعريف متغير عام "تقليدي" بدون هذه القيود، يجب استخدام الكلمة المفتاحية `__gshared` غير الآمنة: [ 14 ]

__gshared int global ; // هذا متغير عام عادي.

جافا

في لغة جافا ، تُعرَّف المتغيرات المحلية للخيوط بواسطة كائن ThreadLocalالفئة . [ 15 ] يحتوي ThreadLocal على متغير من النوع T، [ 15 ] والذي يمكن الوصول إليه عبر دوال get/set. على سبيل المثال، يبدو متغير ThreadLocal الذي يحمل قيمة عددية صحيحة كما يلي:

private static final ThreadLocal <Integer> myThreadLocalInteger = new ThreadLocal <Integer> ( ) ;

على الأقل بالنسبة لـ Oracle/OpenJDK، لا يستخدم هذا النظام التخزين المحلي الأصلي للخيوط، على الرغم من استخدام خيوط نظام التشغيل لجوانب أخرى من ترابط Java. بدلاً من ذلك، يخزن كل كائن Thread خريطة (غير آمنة للخيوط) لكائنات ThreadLocal وقيمها (بدلاً من أن يحتوي كل ThreadLocal على خريطة لكائنات Thread وقيمها، مما يؤدي إلى زيادة في تكلفة الأداء). [ 16 ]

لغات .NET: C# وغيرها

في لغات إطار عمل .NET مثل C# ، يمكن تمييز الحقول الثابتة باستخدام السمة ThreadStatic : [ 17 ] : 898

class FooBar { [ThreadStatic] private static int _foo ; }

في إطار عمل .NET 4.0، تتوفر فئة System.Threading.ThreadLocal<T> لتخصيص وتحميل متغيرات خاصة بالخيوط عند الحاجة. [ 17 ] : 899

class FooBar { private static System.Threading.ThreadLocal <int> _foo ; }

كما تتوفر واجهة برمجة تطبيقات (API) لتخصيص متغيرات خاصة بالخيوط بشكل ديناميكي. [ 17 ] : 899-890

أوبجكت باسكال

في لغة Object Pascal ( Delphi ) أو Free Pascal، يمكن استخدام الكلمة المحجوزة threadvar بدلاً من 'var' لتعريف المتغيرات باستخدام التخزين المحلي للخيط.

var mydata_process : integer ; threadvar mydata_threadlocal : integer ;

أوبجكتيف سي

في Cocoa و GNUstep و OpenStep ، يحتوي كل NSThreadكائن على قاموس محلي خاص بالخيط يمكن الوصول إليه من خلال طريقة الخيط threadDictionary.

NSMutableDictionary * dict = [[ NSThread currentThread ] threadDictionary ]; dict [ @"A key" ] = @"Some data" ;

بيرل

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

استخدم الخيوط ؛ استخدم الخيوط::المشتركة ؛متغيري المحلي $localvar ؛ متغيري المشترك $sharedvar : مشترك ؛

بيور بيسك

في لغة PureBasic، يتم تعريف متغيرات الخيوط باستخدام الكلمة المفتاحية Threaded .

متغيرات مترابطة

بايثون

في إصدار بايثون 2.4 أو أحدث، يمكن استخدام الفئة المحلية في وحدة الخيوط لإنشاء تخزين محلي للخيوط.

استوردنا مكتبة threading ، ثم أنشأنا كائناً محلياً باسم mydata ، وقمنا بتعيين قيمة x إلى 1.

يمكن إنشاء نسخ متعددة من الفئة المحلية لتخزين مجموعات مختلفة من المتغيرات. [ 18 ] وبالتالي، فهي ليست فئة أحادية .

روبي

يمكن للغة روبي إنشاء/الوصول إلى المتغيرات المحلية للخيط باستخدام طرق []=/ :[]

Thread.current [ : user_id ] = 1

الصدأ

يمكن إنشاء متغيرات خاصة بالخيوط في لغة Rust باستخدام thread_local!الماكرو الذي توفره مكتبة Rust القياسية:

استخدم std :: cell :: RefCell ؛ استخدم std :: thread ؛thread_local! ( static FOO : RefCell < u32 > = RefCell :: new ( 1 ));FOO.with ( | f | { assert_eq ! ( * f.borrow ( ), 1 ) ; * f.borrow_mut ( ) = 2 ; } ) ;// يبدأ كل خيط بالقيمة الأولية 1، على الرغم من أن هذا الخيط قد غيّر بالفعل نسخته من القيمة المحلية للخيط إلى 2. let t = thread :: spawn ( move || { FOO.with ( | f | { assert_eq ! ( * f.borrow ( ) , 1 ) ; * f.borrow_mut ( ) = 3 ; }); });// انتظر حتى يكتمل تنفيذ الخيط ثم قم بالخروج عند حدوث حالة ذعر t.join ( ). unwrap ( );// يحتفظ الخيط الأصلي بالقيمة الأصلية 2 على الرغم من أن الخيط الفرعي قد غيّر القيمة إلى 3 لهذا الخيط . FOO.with ( | f | { assert_eq ! ( * f.borrow ( ), 2 ); });

انظر أيضاً

  • OpenMP — مرفق آخر للمعالجة المتعددة للذاكرة المشتركة يدعم التخزين لكل مؤشر ترابط عبر "بنود سمات مشاركة البيانات" (انظر تحت §البنود )

مراجع

  1. بيترك، مات (مايو 2006). "تحت الغطاء" . مجلة أنظمة مايكروسوفت . 11 (5). مؤرشف من الأصل في 9 سبتمبر 2010. تم الاسترجاع في 6 أبريل 2010 .
  2. "مكتبة دعم التزامن - cppreference.com" . en.cppreference.com .
  3. القسم 3.7.2 في معيار C++11
  4. "معلومات خاصة بمترجم لغة C لتطبيق Sun" . دليل مستخدم لغة C، Sun Studio 8. 2004. 2.3 مُحدد التخزين المحلي للخيوط.
  5. "مترجمات XL C/C++" . أغسطس 2010. التخزين المحلي للخيوط (TLS). مؤرشف من الأصل في 11 أبريل 2011.
  6. "التخزين المحلي للخيوط" . دليل GCC 3.3.1 . 2003.
  7. "ملاحظات إصدار LLVM 2.0" . 23 مايو 2007. تحسينات llvm-gcc.
  8. "إضافات لغة Clang - وثائق Clang 3.8" . مقدمة. تشرح هذه الوثيقة إضافات اللغة التي يوفرها Clang. بالإضافة إلى إضافات اللغة المذكورة هنا، يهدف Clang إلى دعم مجموعة واسعة من إضافات GCC. يُرجى مراجعة دليل GCC لمزيد من المعلومات حول هذه الإضافات.
  9. "ملاحظات إصدار مُصرّف Intel® C++ 8.1 لنظام Linux لمعالجات Intel IA-32 و Itanium®" (ملف PDF) . 2004. التخزين المحلي للخيوط. مؤرشف من النسخة الأصلية (ملف PDF) بتاريخ 19 يناير 2015.
  10. 1 2 Visual Studio 2003: "Thread Local Storage (TLS)" . وثائق مايكروسوفت . 5 يونيو 2017.
  11. مُصرّف Intel C++ 10.0 (ويندوز): التخزين المحلي للخيوط
  12. "السمات في Clang - وثائق Clang 3.8" . سلسلة.
  13. ألكسندرسكو، أندريه (6 يوليو 2010). "الفصل 13 - التزامن" . لغة البرمجة D. InformIT. ص 3. تم الاطلاع عليه في 3 يناير 2014 . 
  14. برايت، والتر (12 مايو 2009). "الانتقال إلى Shared" . dlang.org . تم الاسترجاع في 3 يناير 2014 .
  15. 1 2 Bloch 2018 ، ص. 151-155 ، §البند 33: ضع في اعتبارك الحاويات غير المتجانسة الآمنة من حيث النوع.
  16. "كيف يتم تنفيذ ThreadLocal في Java داخليًا؟" . Stack Overflow . Stack Exchange . تم الاطلاع عليه بتاريخ 27 ديسمبر 2015 .
  17. 1 2 3 البحاري 2022 .
  18. "cpython/Lib/_threading_local.py at 3.12 · python/cpython" . GitHub . تم الاطلاع عليه بتاريخ 25 أكتوبر 2023 .

فهرس

  • ألباهاري، جوزيف (2022). دو دييز 10 باختصار (  الطبعة الأولى). أورايلي. ISBN 978-1-098-12195-2.
  • بلوخ، جوشوا (2018). "جافا الفعّالة: دليل لغة البرمجة" (  الطبعة الثالثة). أديسون-ويسلي. ISBN 978-0134685991.