تنفيذ طابور خالٍ من الأقفال
تعرّف إلى تصميم طابور خالٍ من الأقفال لمنتِج واحد ومستهلِك واحد.
تنفيذ طابور خالٍ من الأقفال درس مجاني في C++ Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C++ Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C++ Academy 4 دروس في المجموع.
لماذا نستخدم قوائم انتظار خالية من الأقفال؟
قد تصبح قوائم الانتظار التي تستخدم كائنات المزامنة عنق زجاجة عند ارتفاع التنافس. وتتيح قائمة الانتظار الخالية من الأقفال للمنتجين والمستهلكين إحراز تقدم بالتوازي.
SPSC مقابل MPMC
نوعان:
- SPSC — منتج واحد ومستهلك واحد، وهو الأبسط والأسرع
- MPMC — عدة منتجين وعدة مستهلكين، وهو الأكثر عمومية
يُعد SPSC الخيار الطبيعي عندما تتحكم في الطرفين.
مخطط المخزن المؤقت الحلقي SPSC
مخزن مؤقت دائري يضم مؤشرين: head (للمستهلك) وtail (للمنتج). يحدّث كل طرف مؤشّره الخاص.
template <typename T, size_t N>
class SpscQueue {
T buffer_[N];
std::atomic<size_t> head_{0};
std::atomic<size_t> tail_{0};
public:
bool push(const T& v);
bool pop(T& v);
};إضافة عنصر إلى SPSC
يتحقق المنتج من الخانات المتاحة، ثم يكتب العنصر وينشره بتحديث tail.
bool push(const T& v) {
const size_t t = tail_.load(std::memory_order_relaxed);
const size_t next = (t + 1) % N;
if (next == head_.load(std::memory_order_acquire))
return false; // full
buffer_[t] = v;
tail_.store(next, std::memory_order_release);
return true;
}إزالة عنصر من SPSC
يتحقق المستهلك من وجود بيانات، ثم يقرأها وينشر عملية القراءة بتحديث head.
bool pop(T& v) {
const size_t h = head_.load(std::memory_order_relaxed);
if (h == tail_.load(std::memory_order_acquire))
return false; // empty
v = buffer_[h];
head_.store((h + 1) % N, std::memory_order_release);
return true;
}اقتران ترتيب الذاكرة
تتزامن عملية release-store على tail مع عملية acquire-load على tail لدى المستهلك، والعكس صحيح. ومن دون الترتيب المناسب، قد يُعاد ترتيب عمليات كتابة البيانات لتأتي بعد تحديث المؤشر.
حشو أسطر ذاكرة التخزين المؤقت
لتجنب المشاركة الزائفة، ضع head_ وtail_ في سطرَي ذاكرة تخزين مؤقت منفصلين، يفصل بينهما عادةً 64 بايتًا. استخدم alignas.
alignas(64) std::atomic<size_t> head_{0};
alignas(64) std::atomic<size_t> tail_{0};MPMC: أصعب بكثير
يتطلب تعدد المنتجين أو المستهلكين تنسيقًا إضافيًا، عادةً باستخدام حلقات CAS على مؤشرات مشتركة. توجد تصميمات عديدة، مثل طابور Vyukov وMS-queue والتصميم القائم على Hazard-Pointer.
Boost.Lockfree
يصعب إنشاء طوابير خالية من الأقفال بجودة مناسبة للاستخدام الفعلي. استخدم Boost.Lockfree أو ProducerConsumerQueue من Folly بدلًا من كتابة تنفيذك الخاص.
المفاضلات
الطوابير الخالية من الأقفال:
- إنتاجية أعلى عند التزاحم
- زمن استجابة محدود، إذ لا يوجد انتظار لقفل
- أصعب بكثير في الكتابة وتصحيح الأخطاء
- أخطاء ترتيب الذاكرة صامتة ويصعب اكتشافها
اختبار التعليمات البرمجية الخالية من الأقفال
استخدم ThreadSanitizer (-fsanitize=thread) لاكتشاف تعارضات البيانات. واستخدم اختبارات إجهاد تُدرج فترات نوم عشوائية لكشف أخطاء الترتيب.
متى يكون Mutex كافيًا
لا تحتاج معظم التطبيقات إلى طوابير خالية من الأقفال. قِس الأداء أولًا، فغالبًا ما تحقق الطوابير المحمية بـ mutex والمنفذة جيدًا أداءً كافيًا، ولا سيما عند المعالجة على دفعات.
اختبار سريع
ما المقصود بـ false sharing، ولماذا ينبغي حشو head_ وtail_؟
مراجعة
يستخدم طابور SPSC الخالي من الأقفال مخزنًا مؤقتًا حلقيًا، بحيث يملك المنتج tail ويملك المستهلك head. استخدم ترتيب acquire/release، واحشُ المؤشرين لفصلهما في سطرَي ذاكرة تخزين مؤقت مختلفين. أما في حالة MPMC، ففضّل مكتبة مختبرة.
الأسئلة الشائعة
هل درس «تنفيذ طابور خالٍ من الأقفال» مجاني؟
نعم — نص درس «تنفيذ طابور خالٍ من الأقفال» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C++ Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C++ Academy 4 دروس في المجموع.
ماذا ستتعلم في «تنفيذ طابور خالٍ من الأقفال»؟
تعرّف إلى تصميم طابور خالٍ من الأقفال لمنتِج واحد ومستهلِك واحد. تتمرن على C++ Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C++ Academy؟
لا تُشترط خبرة سابقة. C++ Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «تنفيذ طابور خالٍ من الأقفال»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C++ Academy هذا؟
نعم. كل درس في C++ Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- std::atomic وترتيبات الذاكرة
- أنماط المقارنة والتبديل CAS
- تنفيذ طابور خالٍ من الأقفال
- مؤشرات المخاطر ومشكلة ABA