شرح GVL
سبب محدودية الخيوط
شرح GVL درس مجاني في Ruby Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Ruby Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Ruby Academy 4 دروس في المجموع.
ما هو GVL؟
إن GVL (قفل الآلة الافتراضية العام، وكان يُعرف تاريخيًا باسم GIL) قفل داخل CRuby، وهو مفسر Ruby القياسي.
- يسمح بتنفيذ تعليمات Ruby البرمجية في خيط واحد فقط في كل مرة.
- يحمي المكونات الداخلية للمفسر من التلف الناتج عن التنفيذ المتزامن.
- يعني ذلك أن أعمال وحدة المعالجة المركزية المكتوبة بـ Ruby الخالصة لا تعمل فعليًا بالتوازي على عدة أنوية.
يساعد فهم GVL على توضيح الحالات التي تكون فيها الخيوط مفيدة والحالات التي لا تكون فيها كذلك.
سبب وجوده
لم تُكتب جميع المكونات الداخلية لـ CRuby (مثل تخصيص الكائنات وجمع البيانات المهملة والامتدادات المكتوبة بلغة C) بحيث تكون آمنة مع الخيوط. يجعل GVL هذه المكونات آمنة من خلال تسلسل تنفيذ Ruby.
إزالته بطريقة سليمة أمر صعب، لذلك تحتفظ CRuby به. لا يحتوي JRuby وTruffleRuby على GVL، ويمكنهما تشغيل الخيوط على عدة أنوية.
الأعمال المعتمدة على وحدة المعالجة المركزية
بالنسبة إلى المهام المعتمدة على وحدة المعالجة المركزية (كالرياضيات والفرز والتحليل)، لا تسرّع الخيوط التنفيذ في CRuby. إذ لا ينفذ تعليمات Ruby البرمجية في أي لحظة إلا خيط واحد، لذا يعادل خيطان مخصصان لوحدة المعالجة المركزية تقريبًا خيطًا واحدًا مع تكلفة إضافية.
def heavy
sum = 0
1_000_00.times { |i| sum += i }
sum
end
puts heavy
puts "CPU work does not parallelize under the GVL"يحرر الإدخال والإخراج GVL
الفكرة الأساسية هي أن الإدخال والإخراج الحاجب يحرر GVL. فعندما يدخل خيط في حالة سكون، أو يقرأ ملفًا، أو ينتظر مقبسًا، فإنه يحرر القفل كي يتمكن خيط آخر من تشغيل تعليمات Ruby.
لهذا السبب تكون الخيوط رائعة للأعمال المعتمدة على الإدخال والإخراج حتى مع وجود GVL.
تداخل فترات الانتظار
تدخل الخيوط الثلاثة هنا في حالة سكون كل منها. وبما أن sleep يحرر GVL، تتداخل فترات الانتظار. ويبلغ الوقت الإجمالي نحو 0.2s، وليس 0.6s.
start = Time.now
threads = (1..3).map do
Thread.new { sleep 0.2 }
end
threads.each(&:join)
elapsed = Time.now - start
puts "Elapsed roughly: " + elapsed.round(1).to_sيمكن للامتدادات المكتوبة بلغة C تحريره
يمكن للامتدادات المكتوبة جيدًا بلغة C تحرير GVL أثناء العمليات الحسابية الأصلية الطويلة باستخدام rb_thread_call_without_gvl. ويتيح ذلك تشغيل الأعمال الأصلية الثقيلة بالتوازي بينما تواصل خيوط Ruby عملها.
لذلك تحقق بعض المكتبات توازيًا حقيقيًا حتى على CRuby.
تستمر الخيوط في التبديل
حتى في أعمال وحدة المعالجة المركزية، لا يجمّد GVL الخيوط الأخرى إلى الأبد. إذ ينتزع CRuby التحكم من الخيط دوريًا كي يحصل كل خيط على دوره. يكون العمل متزامنًا (متداخلًا)، لكنه ليس متوازيًا (متزامنًا في اللحظة نفسها).
results = []
mutex = Mutex.new
threads = (1..3).map do |n|
Thread.new do
mutex.synchronize { results << n }
end
end
threads.each(&:join)
puts "Collected: " + results.sort.join(", ")التزامن مقابل التوازي
مصطلحان مهمان:
- التزامن: تتقدم المهام من خلال التداخل فيما بينها. يتيح GVL ذلك.
- التوازي: تعمل المهام في اللحظة نفسها تمامًا على أنوية مختلفة. ويمنع GVL ذلك بالنسبة إلى تعليمات Ruby البرمجية.
تمنحك الخيوط التزامن، بينما تمنحك Ractors التوازي.
تجاوز GVL
لاستخدام عدة أنوية في أعمال وحدة المعالجة المركزية على CRuby، يمكنك:
- تشغيل عدة عمليات (لكل منها GVL خاص بها).
- استخدام Ractors للتوازي داخل العملية.
- إسناد العمل إلى امتدادات مكتوبة بلغة C تحرر GVL.
أما لأعمال الإدخال والإخراج، فعادةً ما تكون الخيوط العادية كافية.
لا تشترك العمليات في GVL
تشغّل كل عملية من عمليات نظام التشغيل آلة Ruby افتراضية خاصة بها مع GVL خاص بها. يؤدي إنشاء عملية فرعية إلى توازي حقيقي، لكن العمليات لا تتشارك الذاكرة وتستهلك موارد أكثر.
puts "Main pid: " + Process.pid.to_s
puts "Each forked process has its own independent GVL"اختيار الاستراتيجية
دليل اتخاذ القرار:
- هل يوجد الكثير من الانتظار؟ استخدم الخيوط.
- هل توجد أعمال كثيفة على وحدة المعالجة المركزية وتريد الاستفادة من الأنوية؟ استخدم العمليات أو Ractors.
- هل تحتاج إلى حالة مشتركة؟ استخدم الخيوط مع
Mutex، لكن انتبه إلى حدود GVL.
يمنعك فهم GVL من الوقوع في الخطأ الشائع المتمثل في توقع توسع خيوط وحدة المعالجة المركزية خطيًا.
تحقق سريع
اختبر فهمك لـ GVL.
مراجعة: GVL
أهم النقاط المتعلقة بقفل الآلة الافتراضية العام:
- ينفذ خيط واحد فقط تعليمات Ruby البرمجية في كل مرة على CRuby.
- يحمي المكونات الداخلية للمفسر.
- يحرره الإدخال والإخراج الحاجب، مما يتيح تداخل فترات الانتظار.
- لا تعمل أعمال Ruby المعتمدة على وحدة المعالجة المركزية بالتوازي عبر عدة أنوية.
- استخدم العمليات أو Ractors لتحقيق توازٍ حقيقي.
سننتقل بعد ذلك إلى Fibers للتعرف على التزامن التعاوني خفيف الوزن.
الأسئلة الشائعة
هل درس «شرح GVL» مجاني؟
نعم — نص درس «شرح GVL» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Ruby Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Ruby Academy 4 دروس في المجموع.
ماذا ستتعلم في «شرح GVL»؟
سبب محدودية الخيوط تتمرن على Ruby Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Ruby Academy؟
لا تُشترط خبرة سابقة. Ruby Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «شرح GVL»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Ruby Academy هذا؟
نعم. كل درس في Ruby Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.