التزامن وتعدد الخيوط في C++: من الألف إلى الياء

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

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

ابدأ الدورة

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



المعالجات الحديثة تحتوي على نوى متعددة، وللاستفادة من طاقتها الكاملة، يجب أن تكون البرامج قادرة على تنفيذ مهام متعددة في وقت واحد. التزامن (concurrency) هو قدرة البرنامج على تنفيذ مهام متعددة في فترات زمنية متداخلة، وتعدد الخيوط (multithreading) هو إحدى طرق تحقيقه، حيث ينقسم البرنامج إلى عدة خيوط تعمل بالتوازي.



في C++، ظهر دعم تعدد الخيوط في المعيار C++11 ومنذ ذلك الحين تطور بنشاط. في هذه المقالة، سنغطي جميع الجوانب الرئيسية: من إنشاء الخيوط إلى أنماط المزامنة المعقدة.



الأساسيات: إنشاء وإدارة الخيوط



std::thread - اللبنة الأساسية



الفئة std::thread تسمح بتشغيل دالة أو كائن قابل للاستدعاء في خيط منفصل. لنلق نظرة على مثال بسيط:



#include <iostream>#include <thread>

void hello() { std::cout << "مرحباً من الخيط " << std::this_thread::get_id() << std::endl;}

int main() { std::thread t(hello); // تشغيل الخيط t.join(); // انتظار انتهاء الخيط return 0;}


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



تمرير الوسائط ودوال لامبدا



يمكن تمرير الوسائط إلى الخيوط واستخدام لامبدا:



#include <thread>#include <string>

void process(int id, const std::string& data) { // معالجة البيانات}

int main() { std::thread t1(process, 1, "بيانات"); std::thread t2([](int x) { // تعبير لامبدا }, 42); t1.join(); t2.join();}


مزامنة الخيوط: حماية البيانات المشتركة



عندما تصل خيوط متعددة إلى نفس البيانات، تنشأ حالة السباق (race condition). لمنع ذلك، تُستخدم آليات المزامنة.



الميوتكس (std::mutex)



الميوتكس هو بدائي مزامنة يضمن أن خيطاً واحداً فقط يمكنه امتلاكه في أي لحظة:



#include <iostream>#include <thread>#include <mutex>

std::mutex mtx;int counter = 0;

void increment() { for (int i = 0; i < 100000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; // وصول محمي }}

int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Counter: " << counter << std::endl; return 0;}


std::lock_guard هو غلاف RAII يلتقط الميوتكس تلقائياً عند الإنشاء ويحرره عند التدمير. هذا يحمي من نسيان تحرير الميوتكس عن طريق الخطأ.



المتغيرات الشرطية (std::condition_variable)



المتغيرات الشرطية تسمح للخيوط بالانتظار لحدوث حدث معين:



#include <queue>#include <condition_variable>

std::queue<int> data_queue;std::mutex queue_mutex;std::condition_variable cv;

void producer() { for (int i = 0; i < 10; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guard<std::mutex> lock(queue_mutex); data_queue.push(i); cv.notify_one(); // إعلام المستهلك }}

void consumer() { while (true) { std::unique_lock<std::mutex> lock(queue_mutex); cv.wait(lock, []{ return !data_queue.empty(); }); int value = data_queue.front(); data_queue.pop(); lock.unlock(); // معالجة

المدونات

توصيات الكتب