التزامن وتعدد الخيوط في C++: المستوى المتقدم

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

التزامن وتعدد الخيوط في C++: المستوى المتقدم

\n\n

تتطلب التطبيقات الحديثة أقصى أداء، وتعدد الخيوط في C++ هو أحد الأدوات الرئيسية لتحقيق ذلك. ومع ذلك، فإن الآليات الأساسية (std::thread، std::mutex) تؤدي غالبًا إلى الحظر، وسباقات البيانات، وانخفاض الكفاءة. في هذه المقالة، سنستعرض التقنيات المتقدمة للعمل مع التزامن في C++: من العمليات الذرية إلى الهياكل الخالية من الأقفال والمهام غير المتزامنة.

\n\n

1. العمليات الذرية وترتيب الذاكرة

\n\n

تعتبر أقفال المزامنة التقليدية آلية ثقيلة. بالنسبة للعدادات البسيطة أو العلامات، من الأفضل استخدام std::atomic. فهو يضمن ذرية القراءة/الكتابة بدون أقفال. ومع ذلك، على المستوى المتقدم، من المهم فهم ترتيب الذاكرة - نموذج الذاكرة في C++ الذي يحدد رؤية التغييرات بين الخيوط.

\n\n

أنظمة ترتيب الذاكرة الرئيسية:

\n
    \n
  • memory_order_relaxed - الحد الأدنى من الضمانات، فقط الذرية.
  • \n
  • memory_order_consume - نادر الاستخدام، يعتمد على البيانات.
  • \n
  • memory_order_acquire - لا يمكن إعادة ترتيب جميع القراءات/الكتابات اللاحقة قبل هذه العملية.
  • \n
  • memory_order_release - تصبح جميع الكتابات السابقة مرئية للخيوط الأخرى بعد هذه العملية.
  • \n
  • memory_order_acq_rel - مزيج من acquire و release.
  • \n
  • memory_order_seq_cst - الأكثر صرامة، تناسق تسلسلي عالمي (افتراضي).
  • \n
\n\n

مثال على الاستخدام:

\n
#include <atomic>\n#include <thread>\n#include <cassert>\n\nstd::atomic<bool> ready{false};\nint data = 0;\n\nvoid producer() {\n    data = 42;                                   // كتابة البيانات\n    ready.store(true, std::memory_order_release); // نشر العلامة\n}\n\nvoid consumer() {\n    while (!ready.load(std::memory_order_acquire)) // انتظار العلامة\n        ;\n    assert(data == 42); // مضمون رؤية data == 42\n}\n\nint main() {\n    std::thread t1(producer);\n    std::thread t2(consumer);\n    t1.join();\n    t2.join();\n    return 0;\n}
\n\n

هنا، يضمن release و acquire أن كتابة data ستحدث قبل تعيين العلامة، والقراءة بعدها. هذا أسرع من المزامنة.

\n\n

2. الهياكل الخالية من الأقفال

\n\n

تتيح الخوارزميات الخالية من الأقفال تجنب الحظر باستخدام العمليات الذرية و CAS (Compare-And-Swap). مثال كلاسيكي هو المكدس الخالي من الأقفال المعتمد على القائمة المرتبطة أحادية الاتجاه.

\n\n

تنفيذ مكدس خالٍ من الأقفال:

\n
#include <atomic>\n#include <memory>\n\ntemplate<typename T>\nclass LockFreeStack {\nprivate:\n    struct Node {\n        T data;\n        Node* next;\n        Node(const T& val) : data(val), next(nullptr) {}\n    };\n    std::atomic<Node*> head{nullptr};\n\npublic:\n    void push(const T& val) {\n        Node* new_node = new Node(val);\n        new_node->next = head.load(std::memory_order_relaxed);\n        while (!head.compare_exchange_weak(\n            new_node->next, new_node,\n            std::memory_order_release,\n            std::memory_order_relaxed)) {\n            // حلقة حتى تنجح CAS\n        }\n    }\n\n    bool pop(T& result) {\n        Node* old_head = head.load(std::memory_order_relaxed);\n        while (old_head != nullptr &&\n               !head.compare_exchange_weak(\n                   old_head, old_head->next,\n                   std::memory_order_acquire,\n                   std::memory_order_relaxed)) {\n            // انتظار\n        }\n        if (old_head == nullptr) return false;\n        result = old_head->data;\n        // تنبيه: في الكود الحقيقي، تحتاج إلى جامع قمامة أو مؤشرات خطر\n        delete old_head;\n        return true;\n    }\n};
\n\n

ملاحظات مهمة:

\n
    \n

المدونات

توصيات الكتب